package terse.talk;

import java.io.File;
import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.regex.Pattern;

import terse.talk.Talk;
import terse.talk.Pro.Dict;
import terse.talk.Pro.Vec;
import terse.talk.Talk.Terp;

public class Async extends Thread {
    String dirname;
    static Pattern OK_FILENAME = Pattern.compile("[A-Za-z0-9_]+");

    HashMap<String, Terp> terps = new HashMap<String, Terp>();
    BlockingQueue<Job> inQueue = new ArrayBlockingQueue<Job>(32);

    static long serial = 10;

    synchronized long getSerial() {
        ++serial;
        return serial;
    }

    Async(String dirname) {
        this.dirname = dirname;
    }

    class Job {
        String path;
        HashMap<String, String> query;
        ArrayBlockingQueue<Result> reply;
        long id;

        Job(String path, HashMap<String, String> query) {
            this.path = path;
            this.query = query;
            this.reply = new ArrayBlockingQueue<Result>(1);
            this.id = getSerial();
        }
        public String toString() {
            return "Job [id=" + id + ", path=" + path + ", query=" + query + ", reply=" + reply + "]";
        }
    }

    Job newJob(String path, HashMap<String, String> query) {
        return new Job(path, query);
    }

    class Result {
        int httpStatus;
        String contentType;
        Dict renderMe;
        Terp terp;

        public Result(int httpStatus, String contentType, Dict renderMe, Terp terp) {
            this.httpStatus = httpStatus;
            this.contentType = contentType;
            this.renderMe = renderMe;
            this.terp = terp;
        }
        public String toString() {
            return "Result [contentType=" + contentType + ", httpStatus=" + httpStatus + ", renderMe=" + renderMe + "]";
        }
    }

    Result newResult(int httpStatus, String contentType, Dict renderMe, Terp terp) {
        return new Result(httpStatus, contentType, renderMe, terp);
    }
    Result errorResult(String msg, Object... objects) {
        Terp tmp = new Terp("", "");  // TODO: eliminate this, by using Json?
        Dict z = tmp.newDict(Talk.pros(tmp.newVec(Talk.pros(tmp.newStr("type"), tmp.newStr("text"))), tmp.newVec(Talk.pros(tmp
                .newStr("value"), tmp.newStr(Talk.fmt(msg, objects))))));
        return newResult(404, "", z, tmp);
    }

    public void run() {
        while (true) {
            Talk.say("Async: waiting to take()");
            Job job;
            try {
                job = inQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
                continue;
            }
            assert job != null;
            Talk.say("Async: took: ", job);

            String imageName = job.query.get("i");
            Talk.say("Async: 1 imageName=: <%s>", imageName);
            if (imageName == null || imageName.equals("")) {
                imageName = "default";
            }
            Talk.say("Async: 2 imageName=: <%s>", imageName);
            Terp terp = terps.get(imageName);
            Talk.say("Async: 3 terp=: <%s>", terp);
            if (terp == null) {
                // TODO: load from .tti file.
                if (!OK_FILENAME.matcher(imageName).matches()) {
                    Result errorReply = errorResult("BAD FILENAME SYNTAX FOR IMAGE: <%s>", imageName);
                    boolean ok = job.reply.offer(errorReply);
                    assert ok;
                    continue;
                }
                Talk.say("Async: new terp for image=<%s>", imageName);
                try {
                    terp = new Terp(imageName, new File(dirname, imageName + ".tti").getPath());
                    Talk.say("Async: 4 terp=: <%s>", terp);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Result errorReply = errorResult("CANNOT LOAD TERP WITH IMAGE: <%s>", imageName);
                    boolean ok = job.reply.offer(errorReply);
                    assert ok;
                    continue;
                }
                terps.put(imageName, terp);
            }

            Talk.say("Async: handling... ", job);
            Dict renderMe = terp.handleUrl(job.path, job.query);
            Result reply = new Result(200, "", renderMe, terp);
            boolean ok = job.reply.offer(reply);
            assert ok : Talk.fmt("WHO PUT MY Q? %s <%s>  <%s>", imageName, job, reply);

            Talk.say("Async: replied.  Looping.");
        }
    }
}
