package com.moex.asts.export;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

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

public class JavaEncoder extends Encoder {

    public JavaEncoder(String filename) throws FileNotFoundException, UnsupportedEncodingException {
        super(null);
        Path path = Paths.get(filename);
        String[] parts = path.getFileName().toString().split("\\.");
        if (parts.length > 2) {
            File root;
            Path parent = path.getParent();
            if (parent == null)
                root = new File(".");
            else
                root = parent.toFile();
            File file = root;
            for (int i = 0; i < parts.length - 2; i++)
                file = new File(file, parts[i]);
            file.mkdirs();
            file = new File(file, parts[parts.length - 2] + ".java");
            writer = new PrintWriter(file);
            writer.print("package ");
            for (int i = 0; i < parts.length - 2; i++) {
                if (i > 0)
                    writer.print('.');
                writer.print(parts[i]);
            }
            writer.println(";");
            writer.println();
        } else
            writer = new PrintWriter(filename);

        writer.println("import java.util.Collections;");
        writer.println("import java.util.LinkedHashMap;");
        writer.println("import java.util.Map;");
        writer.println("import com.moex.memory.Column;");
        writer.println("import com.moex.memory.Type;");
        writer.println();
        writer.print("public class ");
        writer.print(parts[parts.length - 2]);
        writer.println(" {");
        writer.println();
        writer.print("    private ");
        writer.print(parts[parts.length - 2]);
        writer.println("() {}");
        writer.println();
    }

    private final Set<String> names = new LinkedHashSet<>();
    
    @Override
    public void close() throws IOException {
        writer.println();
        
        writer.println("    public final static Map<String, Column[]> TABLES;");
        writer.println();
        writer.println("    static {");
        writer.println("        Map<String, Column[]> map = new LinkedHashMap<>();");
        for (String name : names) {
            writer.print("        map.put(\"");
            writer.print(name);
            writer.print("\", ");
            writer.print(name);
            writer.println(");");
        }
        writer.println("        TABLES = Collections.unmodifiableMap(map);");
        writer.println("    }");
        
        writer.println();
        writer.println('}');
        super.close();
    }

    @Override
    public void encode(TableInfo specs, ClientTable table, Map<String, Object> params) {
        if (specs == Export.SESSION) return;
                
        names.add(specs.alias());
        
        writer.print("    //  ");
        for (int i = 0; i < specs.alias().length(); i++)
            writer.print('=');
        writer.println();
        writer.print("    //  ");
        writer.println(specs.alias());
        writer.print("    //  ");
        for (int i = 0; i < specs.alias().length(); i++)
            writer.print('=');
        writer.println();
        
        writer.print("    public final static String TABLE_");
        writer.print(specs.alias());
        writer.print(" = \"");
        writer.print(specs.alias());
        writer.println("\";");
        writer.println();

        writer.print("    public final static Column[] ");
        writer.print(specs.alias());
        writer.println(" = new Column[] {");
        String[] names;
        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++;
            }
        }
        int count = 0;
        for (int i = 0; i < names.length; i++) {
            if (index[i] < 0)
                continue;
            if (count++ > 0)
                writer.println("),");
            Column c = table.getColumn(index[i]);
            writer.print("            new Column(\"");
            writer.print(names[i]);
            writer.print("\", Type.");
            writer.print(c.type().name());
            if (c.flags() != 0 || c.length() > 0) {
                writer.print(", ");
                writer.print(c.length());
            }
            if (c.flags() != 0) {
                writer.print(", ");
                String or = "";
                if ((c.flags() & Column.PRIMARY) != 0) {
                    writer.print("Column.PRIMARY");
                    or = " | ";
                }
                if ((c.flags() & Column.AUTOTRIM) != 0) {
                    writer.print(or);
                    writer.print("Column.AUTOTRIM");
                    or = " | ";
                }
            }
        }
        if (count > 0)
            writer.println(")");
        writer.println("    };");
        writer.println();
        if (count > 0) {
            count = 0;
            for (int i = 0; i < names.length; i++) {
                if (index[i] < 0)
                    continue;
                writer.print("    public final static int FIELD_");
                writer.print(specs.alias());
                writer.print("_");
                writer.print(names[i]);
                writer.print(" = ");
                writer.print(count++);
                writer.println(";");
            }
            writer.println();
        }
    }

}
