import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

import javax.imageio.ImageIO;

import readers.ColorRange;
import readers.DDRReader;
import readers.ITGReader;
import readers.LoveLiveReader;
import readers.PopnReader;
import readers.Reader;
import readers.SoundVoltexReader;
import readers.TestReader;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.awt.Color;

public class ArcadeReader {
    /*
     * Important data we would like to know for all games:
     * Score
     * Rank (Probably implementation-specific)
     * Note accuracy [List]
     *  Marvelous
     *  Perfect
     *  Great
     *  Good
     *  Bad
     *  OK
     *  Miss
     * Difficulty
     * Song Name / Title
     * Percentage (Can be EX Score, a percentage accuracy or survival percent)
     * Max Combo
     * Other (Not used by the auto detecter, used for storing misc. data.)
     * 
     * Notes about Readers:
     * Love Live
     *  - Does not use: Marvelous, OK
     * Project Diva
     *  - Does not use: Marvelous, OK
     *  - Fail condition is MISSxTAKE
     * DDR
     *  - Does not use: Max Combo (Cannot calculate in combo carryover mode), Bad
     *  - Fail condition is E rank.
     * Pop'n Music
     *  - Does not use: Marvelous, Bad, OK
     *  - Stores number of bars filled in Percentage.
     *  - Fail condition is <17 in the meter (max is 24)
     * Sound Voltex
     *  - Sound Voltex uses the note accuracy list slots as follows: 
     *      Early:
     *          Error - Marvelous
     *          Near - Perfect
     *          Critical - Great
     *          S-Critical - Good
     *          Critical - Bad
     *          Near - OK
     *          Error - Miss
     *  - Sound Voltex stores EX score in Percentage.
     *  - Max Combo is Max Chain
     *  - Sound Voltex will store additional data about accuracy of note types as such:
     *      {"chip":{"scritical":0,"critical":0,"near":0,"error":0},"long":{"scritical":0,"error":0},"vol":{"scritical":0,"error":0},"gauge":<excessive|normal>,"gauge_pct":100}
     *      //Also storing what type of clear gauge was met and the % of the gauge.
     *  - Fail condition is <70% normal gauge.
     * IIDX
     *  - Not going to support right now.
     * 
     */

    static int testCount=0;
    static int testsSucceeded=0;

    final static int DDR_AAA=0;
    final static int DDR_AAPLUS=1;
    final static int DDR_AA=2;
    final static int DDR_AAMINUS=3;
    final static int DDR_APLUS=4;
    final static int DDR_A=5;
    final static int DDR_AMINUS=6;
    final static int DDR_BPLUS=7;
    final static int DDR_B=8;
    final static int DDR_BMINUS=9;
    final static int DDR_CPLUS=10;
    final static int DDR_C=11;
    final static int DDR_CMINUS=12;
    final static int DDR_DPLUS=13;
    final static int DDR_D=14;
    final static int DDR_E=15;
    final static int ITG_QUAD=0;
    final static int ITG_TRI=1;
    final static int ITG_DOUB=2;
    final static int ITG_STAR=3;
    final static int ITG_SPLUS=4;
    final static int ITG_S=5;
    final static int ITG_SMINUS=6;
    final static int ITG_APLUS=7;
    final static int ITG_A=8;
    final static int ITG_AMINUS=9;
    final static int ITG_BPLUS=10;
    final static int ITG_B=11;
    final static int ITG_BMINUS=12;
    final static int ITG_CPLUS=13;
    final static int ITG_C=14;
    final static int ITG_CMINUS=15;
    final static int ITG_D=16;
    final static int ITG_F=17;
    final static int LOVELIVE_SSS=0;
    final static int LOVELIVE_SS=1;
    final static int LOVELIVE_S=2;
    final static int LOVELIVE_A=3;
    final static int LOVELIVE_B=4;
    final static int LOVELIVE_C=5;
    final static int LOVELIVE_D=6;
    final static int POPN_S=0;
    final static int POPN_AAA=1;
    final static int POPN_AA=2;
    final static int POPN_A=3;
    final static int POPN_B=4;
    final static int POPN_C=5;
    final static int POPN_D=6;
    final static int POPN_E=7;
    final static int SDVX_S=0;
    final static int SDVX_AAAPLUS=1;
    final static int SDVX_AAA=2;
    final static int SDVX_AAPLUS=3;
    final static int SDVX_AA=4;
    final static int SDVX_APLUS=5;
    final static int SDVX_A=6;
    final static int SDVX_B=7;
    final static int SDVX_C=8;
    final static int SDVX_D=9;
    static Reader interpret(Path p) {
        BufferedImage img;
        try {
            img = ImageIO.read(p.toFile());
            return interpret(img);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    static Reader interpret(BufferedImage img) {
        if (img.getHeight()>img.getWidth()) {
            return new SoundVoltexReader();
        } else {
            Color col1 = new Color(img.getRGB(31, 41),true);
            ColorRange range1 = new ColorRange(245,255,235,240,70,80);
            Color col2 = new Color(img.getRGB(27, 1012),true);
            ColorRange range2 = new ColorRange(245,255,160,170,195,205);
            if (range1.colorInRange(col1)) {
                return new PopnReader();
            }
            if (col1.equals(Color.BLACK)) {
                return new DDRReader();
            }
            if (col1.equals(Color.WHITE)) {
                return new ITGReader();
            }
            if (range2.colorInRange(col2)) {
                return new LoveLiveReader();
            }
        }
        return null;
    }
    static void test(String filename,Class<?> reader,int score,int rank,int[] notes,int diff,double pct,int maxcombo){
        test(filename,reader,score,rank,notes,diff,pct,maxcombo,"");
    }
    static void test(String filename,Class<?> reader,int score,int rank,int[] notes,int diff,double pct,int maxcombo,String other){
        testCount++;
        Reader r = interpret(Paths.get("tests",filename));
        r.interpretBoxes(Paths.get("tests",filename),true);
        Reader compare = new TestReader(score,rank,notes,diff,pct,maxcombo,other);
        if (!reader.isInstance(r)) {
            ArcadeReader.err(new Exception("Test \""+filename+"\" Failed:\nExpected class type: "+reader+" \nActual:"+r.getClass()));
            return;
        }
        if (!compare.equals(r)) {
            ArcadeReader.err(new Exception("Test \""+filename+"\" Failed:\nExpected:"+compare+"\nActual:"+r));
            return;
        }
        ArcadeReader.success();
    }

    public static void err(Exception e){
        System.out.println(e.getStackTrace()[0]);
        System.out.println(e.getMessage());
        System.exit(1);
    }
    public static void success(){
        testsSucceeded++;
        System.out.println(testsSucceeded+"/"+testCount+"passed!");
    }

    public static void runTests() {
        test("ddr1.png",DDRReader.class,
            994790/*score*/,
            DDR_AAA/*rank*/,
            new int[]{322,77,3,0,96,1,0}/*notes*/,
            9/*difficulty*/,
            0.0/*pct*/,
            287/*maxcombo*/,
            "{\"ex\":1411,\"fast\":18,\"slow\":62,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr2.png",DDRReader.class,
            875800/*score*/,
            DDR_APLUS/*rank*/,
            new int[]{333,196,108,18,10,26,0}/*notes*/,
            11/*difficulty*/,
            0.0/*pct*/,
            230/*maxcombo*/,
            "{\"ex\":1529,\"fast\":105,\"slow\":217,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr3.png",DDRReader.class,
            991000/*score*/,
            DDR_AAA/*rank*/,
            new int[]{414,100,10,1,83,0,0}/*notes*/,
            9/*difficulty*/,
            0.0/*pct*/,
            526/*maxcombo*/,
            "{\"ex\":1701,\"fast\":16,\"slow\":95,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr4.png",DDRReader.class,
            959410/*score*/,
            DDR_AAPLUS/*rank*/,
            new int[]{518,187,53,0,49,10,0}/*notes*/,
            10/*difficulty*/,
            0.0/*pct*/,
            310/*maxcombo*/,
            "{\"ex\":2128,\"fast\":158,\"slow\":82,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr5.png",DDRReader.class,
            996070/*score*/,
            DDR_AAA/*rank*/,
            new int[]{289,47,3,0,11,0,0}/*notes*/,
            12/*difficulty*/,
            0.0/*pct*/,
            339/*maxcombo*/,
            "{\"ex\":997,\"fast\":14,\"slow\":36,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr6.png",DDRReader.class,
            995410/*score*/,
            DDR_AAA/*rank*/,
            new int[]{375,72,2,0,17,1,0}/*notes*/,
            13/*difficulty*/,
            0.0/*pct*/,
            779/*maxcombo*/,
            "{\"ex\":1322,\"fast\":20,\"slow\":54,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr7.png",DDRReader.class,
            940300/*score*/,
            DDR_AA/*rank*/,
            new int[]{334,102,28,3,8,15,0}/*notes*/,
            14/*difficulty*/,
            0.0/*pct*/,
            100/*maxcombo*/,
            "{\"ex\":1258,\"fast\":51,\"slow\":82,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr8.png",DDRReader.class,
            978970/*score*/,
            DDR_AAPLUS/*rank*/,
            new int[]{405,133,21,1,13,2,0}/*notes*/,
            15/*difficulty*/,
            0.0/*pct*/,
            320/*maxcombo*/,
            "{\"ex\":1541,\"fast\":72,\"slow\":83,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr9.png",DDRReader.class,
            891770/*score*/,
            DDR_APLUS/*rank*/,
            new int[]{386,182,90,13,7,27,0}/*notes*/,
            16/*difficulty*/,
            0.0/*pct*/,
            211/*maxcombo*/,
            "{\"ex\":1633,\"fast\":186,\"slow\":99,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr10.png",DDRReader.class,
            867270/*score*/,
            DDR_APLUS/*rank*/,
            new int[]{313,165,92,19,32,32,0}/*notes*/,
            17/*difficulty*/,
            0.0/*pct*/,
            80/*maxcombo*/,
            "{\"ex\":1457,\"fast\":166,\"slow\":110,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("ddr11.png",DDRReader.class,
            201760/*score*/,
            DDR_E/*rank*/,
            new int[]{74,39,23,10,2,18,0}/*notes*/,
            18/*difficulty*/,
            0.0/*pct*/,
            65/*maxcombo*/,
            "{\"ex\":329,\"fast\":62,\"slow\":10,\"playstyle\":\"\",\"diff\":\"\"}"/*other*/);
        test("itg1.png",ITGReader.class,
            8020/*score*/,
            ITG_APLUS/*rank*/,
            new int[]{683,258,354,116,6,18,0}/*notes*/,
            12/*difficulty*/,
            87.28/*pct*/,
            0/*maxcombo*/);
        test("itg2.png",ITGReader.class,
            7563/*score*/,
            ITG_AMINUS/*rank*/,
            new int[]{591,246,405,152,1,36,0}/*notes*/,
            12/*difficulty*/,
            80.40/*pct*/,
            0/*maxcombo*/);
        test("itg3.png",ITGReader.class,
            8012/*score*/,
            ITG_AMINUS/*rank*/,
            new int[]{653,244,344,59,3,42,0}/*notes*/,
            12/*difficulty*/,
            81.90/*pct*/,
            0/*maxcombo*/);
        test("itg4.png",ITGReader.class,
            8228/*score*/,
            ITG_A/*rank*/,
            new int[]{1480,551,679,92,5,72,0}/*notes*/,
            12/*difficulty*/,
            84.95/*pct*/,
            0/*maxcombo*/);
        test("itg5.png",ITGReader.class,
            6241/*score*/,
            ITG_F/*rank*/,
            new int[]{482,185,206,79,10,42,0}/*notes*/,
            12/*difficulty*/,
            60.95/*pct*/,
            0/*maxcombo*/);
        test("itg6.png",ITGReader.class,
            8069/*score*/,
            ITG_APLUS/*rank*/,
            new int[]{252,99,141,30,2,5,0}/*notes*/,
            12/*difficulty*/,
            88.03/*pct*/,
            0/*maxcombo*/);
        test("itg7.png",ITGReader.class,
            9170/*score*/,
            ITG_STAR/*rank*/,
            new int[]{209,58,36,1,1,0,0}/*notes*/,
            10/*difficulty*/,
            97.16/*pct*/,
            0/*maxcombo*/);
        test("itg8.png",ITGReader.class,
            9008/*score*/,
            ITG_SPLUS/*rank*/,
            new int[]{319,75,65,8,3,0,0}/*notes*/,
            13/*difficulty*/,
            95.79/*pct*/,
            0/*maxcombo*/);
        test("itg9.png",ITGReader.class,
            9032/*score*/,
            ITG_STAR/*rank*/,
            new int[]{445,100,95,11,1,0,0}/*notes*/,
            14/*difficulty*/,
            96.08/*pct*/,
            0/*maxcombo*/);
        test("itg10.png",ITGReader.class,
            6921/*score*/,
            ITG_B/*rank*/,
            new int[]{247,108,172,113,27,13,0}/*notes*/,
            15/*difficulty*/,
            74.72/*pct*/,
            0/*maxcombo*/);
        test("itg11.png",ITGReader.class,
            6921/*score*/,
            ITG_B/*rank*/,
            new int[]{247,108,172,113,27,13,0}/*notes*/,
            15/*difficulty*/,
            74.72/*pct*/,
            0/*maxcombo*/);
        test("itg12.png",ITGReader.class,
            3829/*score*/,
            ITG_F/*rank*/,
            new int[]{151,72,80,50,6,17,0}/*notes*/,
            16/*difficulty*/,
            37.37/*pct*/,
            0/*maxcombo*/);
        test("itg13.png",ITGReader.class,
            0/*score*/,
            ITG_F/*rank*/,
            new int[]{0,0,0,0,0,6,0}/*notes*/,
            17/*difficulty*/,
            0.0/*pct*/,
            0/*maxcombo*/);
        test("itg14.png",ITGReader.class,
            0/*score*/,
            ITG_F/*rank*/,
            new int[]{0,0,0,0,0,6,0}/*notes*/,
            18/*difficulty*/,
            0.0/*pct*/,
            0/*maxcombo*/);
        test("itg15.png",ITGReader.class,
            9497/*score*/,
            ITG_DOUB/*rank*/,
            new int[]{215,40,17,0,1,0,0}/*notes*/,
            9/*difficulty*/,
            98.49/*pct*/,
            0/*maxcombo*/);
        test("lovelive1.png",LoveLiveReader.class,
            325415/*score*/,
            LOVELIVE_SSS/*rank*/,
            new int[]{539,4,0,0,0,0,0}/*notes*/,
            0/*difficulty*/,
            119.8/*pct*/,
            543/*maxcombo*/);
        test("lovelive2.png",LoveLiveReader.class,
            331923/*score*/,
            LOVELIVE_SS/*rank*/,
            new int[]{514,96,0,0,0,0,0}/*notes*/,
            0/*difficulty*/,
            116.8/*pct*/,
            610/*maxcombo*/);
        test("lovelive3.png",LoveLiveReader.class,
            282182/*score*/,
            LOVELIVE_SS/*rank*/,
            new int[]{573,45,4,3,8,0,0}/*notes*/,
            0/*difficulty*/,
            115.9/*pct*/,
            334/*maxcombo*/);
        test("lovelive4.png",LoveLiveReader.class,
            282182/*score*/,
            LOVELIVE_SS/*rank*/,
            new int[]{573,45,4,3,8,0,0}/*notes*/,
            0/*difficulty*/,
            115.9/*pct*/,
            334/*maxcombo*/);
        test("lovelive5.png",LoveLiveReader.class,
            19454/*score*/,
            LOVELIVE_SS/*rank*/,
            new int[]{117,11,1,0,0,0,0}/*notes*/,
            0/*difficulty*/,
            117.5/*pct*/,
            74/*maxcombo*/);
        test("lovelive6.png",LoveLiveReader.class,
            290746/*score*/,
            LOVELIVE_SS/*rank*/,
            new int[]{525,104,0,1,1,0,0}/*notes*/,
            0/*difficulty*/,
            116.3/*pct*/,
            427/*maxcombo*/);
        test("lovelive7.png",LoveLiveReader.class,
            0/*score*/,
            LOVELIVE_D/*rank*/,
            new int[]{0,0,0,0,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            0/*maxcombo*/);
        test("lovelive8.png",LoveLiveReader.class,
            140879/*score*/,
            LOVELIVE_A/*rank*/,
            new int[]{445,16,0,0,1,0,0}/*notes*/,
            0/*difficulty*/,
            119.0/*pct*/,
            237/*maxcombo*/);
        test("lovelive9.png",LoveLiveReader.class,
            183644/*score*/,
            LOVELIVE_SSS/*rank*/,
            new int[]{452,10,0,0,0,0,0}/*notes*/,
            0/*difficulty*/,
            119.5/*pct*/,
            462/*maxcombo*/);
        test("lovelive10.png",LoveLiveReader.class,
            143468/*score*/,
            LOVELIVE_S/*rank*/,
            new int[]{410,12,0,1,37,0,0}/*notes*/,
            0/*difficulty*/,
            109.1/*pct*/,
            334/*maxcombo*/);
        test("lovelive11.png",LoveLiveReader.class,
            100456/*score*/,
            LOVELIVE_B/*rank*/,
            new int[]{303,17,0,0,119,0,0}/*notes*/,
            0/*difficulty*/,
            82.3/*pct*/,
            235/*maxcombo*/);
        test("lovelive12.png",LoveLiveReader.class,
            70288/*score*/,
            LOVELIVE_C/*rank*/,
            new int[]{197,16,0,0,206,0,0}/*notes*/,
            0/*difficulty*/,
            54.6/*pct*/,
            213/*maxcombo*/);
        test("popn1.png",PopnReader.class,
            83520/*score*/,
            POPN_A/*rank*/,
            new int[]{495,238,54,44,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            78/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn2.png",PopnReader.class,
            81259/*score*/,
            POPN_B/*rank*/,
            new int[]{482,310,91,36,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            112/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn3.png",PopnReader.class,
            79269/*score*/,
            POPN_B/*rank*/,
            new int[]{481,396,112,37,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            99/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn4.png",PopnReader.class,
            80911/*score*/,
            POPN_B/*rank*/,
            new int[]{485,322,89,40,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            157/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn5.png",PopnReader.class,
            78495/*score*/,
            POPN_B/*rank*/,
            new int[]{425,297,127,35,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            127/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn6.png",PopnReader.class,
            80173/*score*/,
            POPN_B/*rank*/,
            new int[]{438,305,101,26,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            153/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn7.png",PopnReader.class,
            79907/*score*/,
            POPN_B/*rank*/,
            new int[]{474,369,107,34,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            122/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn8.png",PopnReader.class,
            80814/*score*/,
            POPN_B/*rank*/,
            new int[]{496,286,96,50,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            103/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn9.png",PopnReader.class,
            82848/*score*/,
            POPN_A/*rank*/,
            new int[]{528,322,80,28,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            202/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn10.png",PopnReader.class,
            81631/*score*/,
            POPN_B/*rank*/,
            new int[]{532,342,98,32,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            145/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn11.png",PopnReader.class,
            80875/*score*/,
            POPN_B/*rank*/,
            new int[]{500,310,102,36,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            123/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn12.png",PopnReader.class,
            81600/*score*/,
            POPN_B/*rank*/,
            new int[]{524,339,98,33,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            199/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn13.png",PopnReader.class,
            87204/*score*/,
            POPN_A/*rank*/,
            new int[]{665,293,55,18,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            420/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn14.png",PopnReader.class,
            83279/*score*/,
            POPN_A/*rank*/,
            new int[]{493,285,78,27,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            332/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("popn15.png",PopnReader.class,
            86786/*score*/,
            POPN_A/*rank*/,
            new int[]{439,215,32,9,0,0,0}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            276/*maxcombo*/,
            "{\"failed\":false}"/*other*/);
        test("sdvx1.png",SoundVoltexReader.class,
            3019559/*score*/,
            SDVX_D/*rank*/,
            new int[]{13,59,31,495,34,56,582}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            233/*maxcombo*/,
            "{\"ex\":1696,\"chip_scritical\":72,\"chip_critical\":65,\"chip_near\":115,\"chip_error\":240,\"long_scritical\":79,\"long_error\":93,\"vol_scritical\":344,\"vol_error\":262,\"failed\":true}"/*other*/);
        test("sdvx2.png",SoundVoltexReader.class,
            2060643/*score*/,
            SDVX_D/*rank*/,
            new int[]{0,3,2,160,3,0,109}/*notes*/,
        0/*difficulty*/,
            0.0/*pct*/,
            87/*maxcombo*/,
            "{\"ex\":361,\"chip_scritical\":5,\"chip_critical\":5,\"chip_near\":3,\"chip_error\":26,\"long_scritical\":53,\"long_error\":79,\"vol_scritical\":102,\"vol_error\":4,\"failed\":true}"/*other*/);
        test("sdvx3.png",SoundVoltexReader.class,
            8759328/*score*/,
            SDVX_A/*rank*/,
            new int[]{14,88,141,1460,155,156,130}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            257/*maxcombo*/,
            "{\"ex\":5816,\"chip_scritical\":408,\"chip_critical\":296,\"chip_near\":244,\"chip_error\":67,\"long_scritical\":423,\"long_error\":11,\"vol_scritical\":629,\"vol_error\":66,\"failed\":true}"/*other*/);
        test("sdvx4.png",SoundVoltexReader.class,
            9129755/*score*/,
            SDVX_APLUS/*rank*/,
            new int[]{7,84,94,1474,99,86,75}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            234/*maxcombo*/,
            "{\"ex\":4882,\"chip_scritical\":274,\"chip_critical\":193,\"chip_near\":170,\"chip_error\":32,\"long_scritical\":308,\"long_error\":13,\"vol_scritical\":892,\"vol_error\":37,\"failed\":true}"/*other*/);
        test("sdvx5.png",SoundVoltexReader.class,
            9653667/*score*/,
            SDVX_AAPLUS/*rank*/,
            new int[]{1,53,88,1517,21,12,26}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            593/*maxcombo*/,
            "{\"ex\":4119,\"chip_scritical\":173,\"chip_critical\":109,\"chip_near\":65,\"chip_error\":4,\"long_scritical\":375,\"long_error\":1,\"vol_scritical\":969,\"vol_error\":22,\"failed\":false}"/*other*/);
        test("sdvx6.png",SoundVoltexReader.class,
            9498351/*score*/,
            SDVX_AA/*rank*/,
            new int[]{2,70,142,1782,43,29,55}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            372/*maxcombo*/,
            "{\"ex\":5525,\"chip_scritical\":341,\"chip_critical\":185,\"chip_near\":99,\"chip_error\":8,\"long_scritical\":345,\"long_error\":17,\"vol_scritical\":1096,\"vol_error\":32,\"failed\":false}"/*other*/);
        test("sdvx7.png",SoundVoltexReader.class,
            9498351/*score*/,
            SDVX_AA/*rank*/,
            new int[]{2,70,142,1782,43,29,55}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            372/*maxcombo*/,
            "{\"ex\":5525,\"chip_scritical\":341,\"chip_critical\":185,\"chip_near\":99,\"chip_error\":8,\"long_scritical\":345,\"long_error\":17,\"vol_scritical\":1096,\"vol_error\":32,\"failed\":false}"/*other*/);
        test("sdvx8.png",SoundVoltexReader.class,
            725020/*score*/,
            SDVX_D/*rank*/,
            new int[]{8,15,4,148,11,16,706}/*notes*/,
            0/*difficulty*/,
            0.0/*pct*/,
            21/*maxcombo*/,
            "{\"ex\":457,\"chip_scritical\":13,\"chip_critical\":15,\"chip_near\":31,\"chip_error\":201,\"long_scritical\":44,\"long_error\":241,\"vol_scritical\":91,\"vol_error\":272,\"failed\":true}"/*other*/);
    }
    public static int getScore(String record) {
        String searchString = "\"score\":";
        int index = record.indexOf(searchString)+searchString.length();
        int endIndex=record.indexOf(",",index);
        return Integer.parseInt(record.substring(index,endIndex));
    }
    public static String convertTitle(String str) {
        return str.replaceAll(Pattern.quote("#"),"")
        .replaceAll(Pattern.quote("%"),"")
        .replaceAll(Pattern.quote("&"),"")
        .replaceAll(Pattern.quote("{"),"")
        .replaceAll(Pattern.quote("}"),"")
        .replaceAll(Pattern.quote("\\"),"")
        .replaceAll(Pattern.quote("<"),"")
        .replaceAll(Pattern.quote(">"),"")
        .replaceAll(Pattern.quote("*"),"")
        .replaceAll(Pattern.quote("\""),"")
        .replaceAll(Pattern.quote("?"),"")
        .replaceAll(Pattern.quote("'"),"")
        .replaceAll(Pattern.quote("/"),"")
        .replaceAll(Pattern.quote("$"),"")
        .replaceAll(Pattern.quote("!"),"")
        .replaceAll(Pattern.quote(":"),"")
        .replaceAll(Pattern.quote("@"),"")
        .replaceAll(Pattern.quote("+"),"")
        .replaceAll(Pattern.quote("`"),"")
        .replaceAll(Pattern.quote("|"),"")
        .replaceAll(Pattern.quote("="),"");
    }
    public static void submitToDatabase(Path p) {
        //First we read and interpret what this image is.
        BufferedImage img;
        try {
            img = ImageIO.read(p.toFile());
            Reader newImg = interpret(img);
            //Path getGamePath = gamePath(newImg);
            newImg.interpretBoxes(p);
            HashMap<String,HashMap<String,List<String>>> DATA = sigPlace.SONG_DATABASE;
            List<String> RECENT_PLAYS = sigPlace.RECENT_PLAYS;
            List<String> RECENT_RECORDS = sigPlace.RECENT_RECORDS;
            HashMap<String,List<String>> SONG_DATA=DATA.getOrDefault(newImg.getClass().getSimpleName(),new HashMap<>());
            List<String> RECORDS = SONG_DATA.getOrDefault(convertTitle(newImg.getTitle()),new ArrayList<>());
            boolean found=false;
            for (int i=0;i<RECORDS.size();i++) {
                String s = RECORDS.get(i);
                int score = getScore(s);
                if (score<newImg.getScore()) {
                    //Insert here.
                    found=true;
                    if (i==0) {
                        RECENT_RECORDS.add(0,newImg.toString());
                    }
                    RECENT_PLAYS.add(0,newImg.toString());
                    if (RECENT_PLAYS.size()>20) {
                        RECENT_PLAYS.remove(RECENT_PLAYS.size()-1);
                    }
                    RECORDS.add(i, newImg.toString());
                    break;
                }
            }
            if (!found) {
                RECORDS.add(newImg.toString());
                RECENT_RECORDS.add(0,newImg.toString());
                RECENT_PLAYS.add(0,newImg.toString());
                if (RECENT_PLAYS.size()>20) {
                    RECENT_PLAYS.remove(RECENT_PLAYS.size()-1);
                }
            }
            if (RECENT_RECORDS.size()>20) {
                RECENT_RECORDS.remove(RECENT_RECORDS.size()-1);
            }
            SONG_DATA.put(convertTitle(newImg.getTitle()),RECORDS);
            DATA.put(newImg.getClass().getSimpleName(),SONG_DATA);

            new Thread(){
                public void run() {
                    //Threaded database update.
                    for (String reader : sigPlace.SONG_DATABASE.keySet()) {
                        for (String song : sigPlace.SONG_DATABASE.get(reader).keySet()) {
                            List<String> data = sigPlace.SONG_DATABASE.get(reader).get(song);
                            try {
                                Files.write(Paths.get("database",reader,song), data, Charset.defaultCharset(), StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING,StandardOpenOption.WRITE);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    List<String> data = sigPlace.RECENT_PLAYS;
                    try {
                        Files.write(Paths.get("database","recentplays"), data, Charset.defaultCharset(), StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING,StandardOpenOption.WRITE);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    data = sigPlace.RECENT_RECORDS;
                    try {
                        Files.write(Paths.get("database","recentrecords"), data, Charset.defaultCharset(), StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING,StandardOpenOption.WRITE);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}