package readers.fonts;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.awt.Color;

public class Font {
    Glyph[] data = new Glyph[10];
    final static int TRANSPARENT = new Color(0,0,0,0).getRGB();
    final public static Font FONT_DDR_DIFF = LoadFont("ddr_diff");
    final public static Font FONT_DDR_EXCOMBO = LoadFont("ddr_excombo");
    final public static Font FONT_DDR_NOTECOUNT = LoadFont("ddr_noteCount");
    final public static Font FONT_DDR_SCORE = LoadFont("ddr_score");
    final public static Font FONT_ITG_DIFF = LoadFont("itg_diff");
    final public static Font FONT_ITG_EX = LoadFont("itg_ex");
    final public static Font FONT_ITG_NOTECOUNT = LoadFont("itg_noteCount");
    final public static Font FONT_ITG_PCT = LoadFont("itg_pct");
    final public static Font FONT_LOVELIVE_NOTECOUNT = LoadFont("lovelive_noteCount");
    final public static Font FONT_LOVELIVE_PCT = LoadFont("lovelive_pct");
    final public static Font FONT_LOVELIVE_SCORE = LoadFont("lovelive_score");
    final public static Font FONT_POPN_COMBO = LoadFont("popn_combo");
    final public static Font FONT_POPN_NOTECOUNT = LoadFont("popn_noteCount");
    final public static Font FONT_POPN_SCORE = LoadFont("popn_score");
    final public static Font FONT_SDVX_EXSCORE = LoadFont("sdvx_EXScore");
    final public static Font FONT_SDVX_LITTLESCORE = LoadFont("sdvx_littleScore");
    final public static Font FONT_SDVX_NOTECOUNT = LoadFont("sdvx_noteCount");
    final public static Font FONT_SDVX_BIGSCORE = LoadFont("sdvx_largeScore");

    public static Font LoadFont(String fontName) {
        Path f = Paths.get("readers","fonts",fontName);
        if (Files.exists(f)) {
            Font font = new Font();
            try {
                List<String> data = Files.readAllLines(f);
                for (int i=0;i<data.size();i+=3) {
                    Glyph g = new Glyph();
                    g.width=Integer.parseInt(data.get(i));
                    g.height=Integer.parseInt(data.get(i+1));
                    String dataArr = data.get(i+2);
                    g.data=new boolean[dataArr.length()];
                    for (int j=0;j<dataArr.length();j++) {
                        g.data[j]=(dataArr.charAt(j))=='1'?true:false;
                    }
                    if (g.width==0||g.height==0) {
                        System.out.println(" WARNING! Font "+fontName+" does not have glyph data for "+(i/3)+"!");
                    }
                    font.data[i/3]=g;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return font;
        } else {
            try {
                Files.createFile(f);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new Font();
    }

    public static void TrainFont(String fontName,String expectedGlyphs,BufferedImage data) {
        Path f = Paths.get("readers","fonts",fontName);
        Font font = LoadFont(fontName);
        int startX=-1;
        int endX=-1;
        outer:
        for (int x=0;x<data.getWidth();x++) {
            for (int y=0;y<data.getHeight();y++) {
                if (data.getRGB(x, y)!=TRANSPARENT) {
                    if (startX==-1) {
                        startX=x;
                    }
                    continue outer;
                }
            }
            //If we got to here it means we found transparent on everything.
            if (startX!=-1) {
                endX=x;
                //Create a Glyph out of this.
                Glyph g = new Glyph();
                g.width=endX-startX;
                g.height=data.getHeight();
                int[] arr = data.getRGB(startX,0,g.width,g.height,null,0,g.width);
                //Find the min and max Y.
                int startY=g.height;
                int endY=0;
                for (int X=0;X<g.width;X++) {
                    for (int Y=0;Y<g.height;Y++) {
                        if (arr[Y*g.width+X]!=TRANSPARENT&&Y<startY) {
                            startY=Y;
                        }
                        if (arr[Y*g.width+X]!=TRANSPARENT&&Y>endY) {
                            endY=Y;
                        }
                    }
                }
                g.height=endY-startY;
                g.data = new boolean[g.width*g.height];
                for (int X=0;X<g.width;X++) {
                    for (int Y=0;Y<g.height;Y++) {
                        g.data[Y*g.width+X]=arr[(Y+startY)*g.width+X]!=TRANSPARENT;
                    }
                }
                int index = expectedGlyphs.charAt(0)-'0';
                if (index>=0&&index<font.data.length) {
                    font.data[index]=g;
                    System.out.println("Glyph for number "+index+" has been saved.");
                } else {
                    System.out.println("Skip unrecognized glyph: "+expectedGlyphs.charAt(0));
                }
                expectedGlyphs=expectedGlyphs.substring(1);
                startX=-1;
                endX=-1;
            }
        }
        saveFont(font,fontName);
    }

    private static void saveFont(Font f,String name) {
        StringBuilder sb = new StringBuilder();
        for (int i=0;i<f.data.length;i++) {
            Glyph g = f.data[i];
            if (g==null) {
                g=new Glyph();
            }
            sb
            .append(g.width)
            .append('\n')
            .append(g.height)
            .append('\n');
            for (int j=0;j<g.data.length;j++) {
                sb.append(g.data[j]?"1":"0");
            }
            sb.append('\n');
        }
        try {
            Files.write(Paths.get("readers","fonts",name),sb.toString().getBytes(),StandardOpenOption.TRUNCATE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public String convertGlyphs(List<Glyph> glyphs) {
        return convertGlyphs(glyphs,false);
    }

    public String convertGlyphs(List<Glyph> glyphs,boolean debug) {
        StringBuilder sb = new StringBuilder();
        for (int i=0;i<glyphs.size();i++) {
            int bestScore=Integer.MIN_VALUE;
            int bestGlyph=0;
            for (int j=0;j<data.length;j++) {
                Glyph g_a = glyphs.get(i);
                Glyph g_b = data[j];
                int score=0;
                for (int x=0;x<g_a.width;x++) {
                    for (int y=0;y<g_a.height;y++) {
                        boolean found=false;
                        //We can be up to 1 pixel off and still be valid.
                        inner:
                        for (int xx=-1;xx<=1;xx++) {
                            for (int yy=-1;yy<=1;yy++) {
                                int a_x=x+xx;
                                int a_y=y+yy;
                                if (a_x>=0&&a_x<g_b.width&&a_y>=0&&a_y<g_b.height) {
                                    int a_index=a_y*g_b.width+a_x;
                                    if (g_a.data[y*g_a.width+x]==g_b.data[a_index]) {
                                        found=true;
                                        break inner;
                                    }
                                }
                            }
                        }
                        if (found) {
                            score++;
                        } else {
                            if (debug) {
                                System.out.print("-");
                            }
                            score-=2;
                        }
                    }
                }
                if (g_b.width-g_a.width>2) {
                    score-=(g_b.width-g_a.width)*g_b.height;
                } else
                if (g_b.height-g_a.height>2) {
                    score-=(g_b.height-g_a.height)*g_b.width;
                }
                if (score>bestScore) {
                    bestScore=score;
                    bestGlyph=j;
                    if (debug) {
                        System.out.println("Glyph "+j+" has a score of "+bestScore+".");
                    }
                } else {
                    if (debug) {
                        System.out.println("  Glyph "+j+" has a score of "+score+".");
                    }
                }
            }
            sb.append(bestGlyph);
        }
        if (debug) {
            System.out.println(sb.toString()+"\n========");
        }
        return sb.toString();
    }
}