package com.moex.asts.export;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import com.moex.asts.export.Description.FieldInfo;
import com.moex.asts.export.Description.TableInfo;
import com.moex.memory.Record;

public class JsonEncoder extends Encoder {

    JsonEncoder(String filename) {
        super(filename);
    }

    @Override
    protected PrintWriter setup() throws FileNotFoundException, UnsupportedEncodingException {
        PrintWriter result = super.setup();
        result.println('{');
        return result;
    }

    @Override
    public void close() throws IOException {
        if (writer != null) {
            writer.println();
            writer.println('}');
        }
        super.close();
    }

    static String toJSON(CharSequence text) {
        StringBuilder s = new StringBuilder();
        final int l = text.length();
        char b, c = 0;
        for (int i = 0; i < l; i++) {
            b = c;
            c = text.charAt(i);
            switch (c) {
                case '\\':
                case '\"':
                    s.append('\\').append(c);
                    break;
                case '/':
                    if (b == '<') s.append('\\');
                    s.append(c);
                    break;
                case '\b':
                    s.append("\\b");
                    break;
                case '\t':
                    s.append("\\t");
                    break;
                case '\n':
                    s.append("\\n");
                    break;
                case '\r':
                    s.append("\\r");
                    break;
                case '\f':
                    s.append("\\f");
                    break;
                case '\0':
                    s.append("\\u0000");
                    break;
                default:
                    s.append(c);
            }
        }
        return s.toString();
    }

    private int count = 0;

    @Override
    public void encode(TableInfo specs, ClientTable table, Map<String, Object> params) {
        if (count++ > 0)
            writer.println(',');

        writer.print("\t\"");
        writer.print(specs.alias());
        writer.println("\": {");

        if (params.size() > 0) {
            writer.println("\t\t\"params\": {");
            int index = 0;
            for (Entry<String, Object> param : params.entrySet()) {
                writer.print("\t\t\t\"");
                writer.print(param.getKey());
                writer.print("\": ");

                Object data = param.getValue();
                if (data == null)
                    writer.print("null");
                else {
                    if (data instanceof CharSequence) {
                        writer.print('"');
                        writer.print(toJSON((CharSequence) data));
                        writer.print('"');
                    } else
                        writer.print(data.toString());
                }
                if (++index < params.size())
                    writer.print(',');
                writer.println();
            }
            writer.println("\t\t},");
        }

        writer.println("\t\t\"values\": [");
        final String[] names;
        final int[] index;
        if (specs.fields.length == 0) {
            names = table.getColumnNames();
            index = new int[names.length];
            for (int j = 0; j < names.length; j++)
                index[j] = j;
        } else {
            names = new String[specs.fields.length];
            index = new int[specs.fields.length];
            int j = 0;
            for (FieldInfo field : specs.fields) {
                names[j] = field.alias();
                index[j] = table.getColumnIndex(field.source);
                j++;
            }
        }

        Iterator<Record> i = table.hasPrimaryIndex()
                ? table.getPrimaryIndex().iterator()
                : table.iterator();
        while (i.hasNext()) {
            final Record r = i.next();
            writer.print("\t\t\t{");
            int field = 0;
            for (int j = 0; j < names.length; j++) {
                if (index[j] < 0)
                    continue;

                if (field++ > 0)
                    writer.print(", ");
                writer.print('"');
                writer.print(names[j]);
                writer.print("\": ");

                if (r.isNull(index[j]))
                    writer.print("null");
                else {
                    switch (table.getColumn(index[j]).type()) {
                        case STRING:
                        case DATE:
                        case TIME:
                            writer.print('"');
                            writer.print(toJSON(r.asString(index[j])));
                            writer.print('"');
                            break;
                        default:
                            writer.print(toJSON(r.asString(index[j])));
                    }
                }
            }
            writer.print("}");
            if (i.hasNext())
                writer.print(',');
            writer.println();
        }
        writer.println("\t\t]");

        writer.print("\t}");
    }

}
