package turgen.plugin.plturbo;

import turgen.plugin.*;
import turgen.generator.SignalGenerator;
import turgen.config.Configuration;
import turgen.*;
import java.io.*;
import javax.swing.*;
import turgen.config.ConfigClass;
import turgen.config.IntegerConfigEntry;
import turgen.dtb.DOS2Binary;
import turgen.dtb.Segment;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.*;

public class PlLowerSilesiaTurbo2000Plugin implements Plugin {

    private PlLowerSilesiaTurbo2000PluginPanel panel;
    private final int[] casChunkName = {116, 114, 112, 57};

    private ConfigClass cfcLowerSilesiaTurbo2000;
    private IntegerConfigEntry cfgPilotLenA;
    private IntegerConfigEntry cfgPilotLenB;
    private IntegerConfigEntry cfgPilotLenSegmentHeader;
    private IntegerConfigEntry cfgSilenceAfterLoader;

    /**
     * New instance
     */
    public PlLowerSilesiaTurbo2000Plugin() {

    }

    /**
     * Get plugin name
     *
     * @return Plugin name
     */
    public String getName() {
        return "Lower Silesia Turbo 2000";

    }

    /**
     * **********************************************************
     */
    /**
     * Get generator instructions
     *
     * @param _pl Playlist item
     * @return Instructions
     */
    public int[] convertItem(AbstractPlayListItem _pl) throws Exception {

        /*Get playlist item*/
        PlLowerSilesiaTurbo2000PlaylistItem pl = (PlLowerSilesiaTurbo2000PlaylistItem) _pl;

        /*Output stream for instructions*/
        InstructionStream is = new InstructionStream();

        /*Nastaveni, pro vsechny mozne konverze je nastaveni stejne,
         *bloky nebo soubory jsou vzdy v Super Turbu o dane rychlosti
         */
        Utils.addSetupInstruction(is, pl.pulses, casChunkName, SignalGenerator.FLAG_POLARITY_10, SignalGenerator.FLAG_ORDER_HL);

        /*Nyni se vybere konverze podle typu v polozce playlistu*/
        switch (pl.conversionType) {

            /*Zde se prevadi cely soubor*/
            case PlLowerSilesiaTurbo2000PlaylistItem.CV_BINARY_AUTOTURBO: {
                convertAutoTurbo(is, pl);
                break;
            }
            case PlLowerSilesiaTurbo2000PlaylistItem.CV_BINARY_BINLOAD_A: {
                convertBinaryLoad(is, pl);
                break;
            }
        }

        is.add(SignalGenerator.INSTR_END);

        return is.getInstructions();

    }

    public AbstractPlayListItem getItemFromPanel() {
        PlLowerSilesiaTurbo2000PlaylistItem dpr = panel.getRecord();
        dpr.pluginClass = Turgen.pluginManager.getPluginClassName(this);
        return dpr;
    }

    public void resetPanel() {
        panel.clear();
    }

    public String validateItemOnPanel() {
        return panel.verify();
    }

    public JPanel getItemPanel() {
        return panel;
    }

    public void init() {
        panel = new PlLowerSilesiaTurbo2000PluginPanel();

        /*Initialize configuration class*/
        cfcLowerSilesiaTurbo2000 = new ConfigClass("LowerSilesiaTurbo2000", "Lower Silesia Turbo 2000");

        /*Initialize configuration entries*/
        cfgPilotLenA = new IntegerConfigEntry("pilotLenA", "Header block pilot tone length [pulse]", 3072, 256, 8192);
        cfgPilotLenB = new IntegerConfigEntry("pilotLenB", "Data block pilot tone length [pulse]", 3072, 256, 8192);
        cfgPilotLenSegmentHeader = new IntegerConfigEntry("pilotLenSegmentHeader", "Segment header pilot tone length [pulse]", 384, 128, 768);
        cfgSilenceAfterLoader = new IntegerConfigEntry("silenceAfterLoader", "Silence after loader [s]", 2, 0, 16);

        /*Add entries to the class*/
        cfcLowerSilesiaTurbo2000.addEntry(cfgPilotLenA);
        cfcLowerSilesiaTurbo2000.addEntry(cfgPilotLenB);
        cfcLowerSilesiaTurbo2000.addEntry(cfgPilotLenSegmentHeader);
        cfcLowerSilesiaTurbo2000.addEntry(cfgSilenceAfterLoader);


        /*Register configuration class*/
        Configuration.registerClass(cfcLowerSilesiaTurbo2000);

    }

    public void fillPanel(AbstractPlayListItem plr) {
        PlLowerSilesiaTurbo2000PlaylistItem dpr = (PlLowerSilesiaTurbo2000PlaylistItem) plr;
        panel.fill(dpr);
    }

    public void setInOutProvider(InOutProvider prov) {
        panel.setIOProvider(prov);

    }

    private void convertAutoTurbo(InstructionStream is, PlLowerSilesiaTurbo2000PlaylistItem cpi) throws Exception {

        /*DATA*/
        int[] fileData = readFileData(cpi.inFile, 0, Integer.MAX_VALUE);
        convertAutoTurboImpl(is, cpi.fname, fileData);
    }

    private void convertAutoTurboImpl(InstructionStream is, String filename, int[] fileData) {

        int plenA = cfgPilotLenA.getAsInt();
        int plenB = cfgPilotLenA.getAsInt();

        int hdrLoad = 0;
        int hdrRun = 0;
        int hdrLength = fileData.length;
        int hdrType = 4;

        /*Header*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(plenA);
        is.add(SignalGenerator.INSTR_SYNC);

        is.add(SignalGenerator.INSTR_DATA);
        is.add(19);

        int[] hdr = Utils.createT2KHeader(filename, hdrType, hdrLoad, hdrLength, hdrRun);
        is.add(hdr);

        /*Safety pulse + safety pause*/
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_BLOCKSEP);

        /*Main block*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(plenB);
        is.add(SignalGenerator.INSTR_SYNC);

        is.add(SignalGenerator.INSTR_DATA);
        is.add(fileData.length + 2);

        int chsum = 255;
        is.add(chsum);
        for (int i = 0; i < fileData.length; i++) {
            chsum ^= fileData[i];
        }
        is.add(fileData);
        is.add(chsum);
        /*Safety pulse + safety pause*/
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_BLOCKSEP);
    }

    private int[] readFileData(String file, int skipBytes, int maxLen) throws Exception {

        byte[] b;
        RandomAccessFile raf = new RandomAccessFile(file, "r");

        int flen = (int) raf.length();
        if (flen > maxLen) {
            raf.close();
            throw new FileTooLargeException("File exceeds " + Integer.toString(maxLen) + " bytes");
        }

        b = new byte[flen - skipBytes];
        raf.skipBytes(skipBytes);
        raf.readFully(b);
        raf.close();


        /*Konverze na int*/
        int[] fileData = new int[b.length];
        for (int i = 0; i < b.length; i++) {
            fileData[i] = b[i] < 0 ? b[i] + 256 : b[i];
        }

        return fileData;
    }

    public String[] getSupportedTapeImageChunks() {
        return new String[]{"trp9"};
    }

    public void addInstructionsForChunk(InstructionStream is, TapeImageChunk chunk) throws Exception {

        /*trXX chunk*/
        if (chunk instanceof TurboChunk) {
            Utils.addSetupInstruction(is, Turgen.pulseCache.getPulsesForPlugin(this)[0], this.casChunkName, SignalGenerator.FLAG_POLARITY_10, SignalGenerator.FLAG_ORDER_HL);
            return;
        }

        /*Only data chunk accepted*/
        if (!(chunk instanceof DataChunk)) {
            return;
        }
        DataChunk dataChunk = (DataChunk) chunk;

        /*Zavadeci ton a data bloku*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(dataChunk.getAux());

        /*Synchropulz*/
        is.add(SignalGenerator.INSTR_SYNC);

        /*Data bloku*/
        is.add(SignalGenerator.INSTR_DATA);
        is.add(dataChunk.getLength());

        int[] dta = dataChunk.getData();

        is.add(dta);


        /*Safety pulse + safety pause*/
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_BLOCKSEP);

    }

    public BinaryConvertor[] getBinaryConvertors() {
        BinaryConvertor[] bc = new BinaryConvertor[2];
        PlLowerSilesiaTurbo2000AutoTurboBinaryConvertor abc = new PlLowerSilesiaTurbo2000AutoTurboBinaryConvertor(this);
        PlLowerSilesiaTurbo2000BinaryLoadABinaryConvertor bla = new PlLowerSilesiaTurbo2000BinaryLoadABinaryConvertor(this);
        bc[0] = abc;
        bc[1] = bla;
        return bc;
    }

    public String getCountry() {
        return "PL";
    }

    public boolean isUsingAlternateFileChooser() {
        return false;
    }

    private void convertBinaryLoad(InstructionStream is, PlLowerSilesiaTurbo2000PlaylistItem pl) throws Exception {

        int segmentHeaderPulses = cfgPilotLenSegmentHeader.getAsInt();

        /*Prepend loader*/
        int[] loaderData = new int[lowerSilesiaTurbo2000BinaryLoaderData.length];
        System.arraycopy(lowerSilesiaTurbo2000BinaryLoaderData, 0, loaderData, 0, loaderData.length);

        /*Change loader skeleton to display name*/
        char[] nameChars = new char[10];
        for (int i = 0; i < nameChars.length; i++) {
            nameChars[i] = ' ';
        }
        String fname = pl.fname;
        if (fname.length() > 10) {
            fname = fname.substring(0, 10);
        }
        char[] fnameChars = fname.toCharArray();
        System.arraycopy(fnameChars, 0, nameChars, 0, fnameChars.length);

        /*Place name*/
        for (int i = 0; i < 10; i++) {
            loaderData[12 + (0x966 - 0x700) + i] = (int) nameChars[i];
        }
        convertAutoTurboImpl(is, fname, loaderData);

        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(cfgSilenceAfterLoader.getAsInt());

        /*Load and analyze binary file*/
        DOS2Binary dtb = new DOS2Binary((pl.inFile));
        dtb.analyzeFromFile();

        int[] silenceList = pl.silenceList;
        if (silenceList == null) {
            silenceList = new int[0];
        }
        int silenceIndex = 0;

        int blockPilotPulses = cfgPilotLenB.getAsInt();

        /*Prepare array of segments*/
        Segment[] segments = new Segment[dtb.getTotalSegmentCount()];
        for (int i = 0; i < segments.length; i++) {
            segments[i] = (Segment) dtb.allSegments.get(i);
        }

        /*Generate first dummy block*/
        int[] firstHead = {0xE0, 0x02};
        addDataBlock(is, firstHead, segmentHeaderPulses);

        /*Generate segment header + segment data blocks*/
        int segCount = segments.length;
        for (int i = 0; i < segCount; i++) {

            Segment seg = segments[i];
            generateSegment(seg, blockPilotPulses, segmentHeaderPulses, is);

            /*Silence after INIT segments*/
            if (seg.type == Segment.INIT_SEGMENT || seg.type == Segment.RUNINIT_SEGMENT) {
                if (silenceIndex < silenceList.length) {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(Math.min(silenceList[silenceIndex], 240));
                    silenceIndex++;
                }
            }

        }
        /*Generate termination header*/
        generateTermHeader(segmentHeaderPulses, is);
    }

    private void generateSegment(Segment seg, int pilotTonePulses, int segmentHeaderPulses, InstructionStream is) throws Exception {
        int[] bHeader = new int[4];
        int sLo = seg.start % 256;
        int sHi = seg.start / 256;
        int eLo = (seg.stop) % 256;
        int eHi = (seg.stop) / 256;

        bHeader[0] = sLo;
        bHeader[1] = sHi;
        bHeader[2] = eLo;
        bHeader[3] = eHi;

        /*Address*/
        addDataBlock(is, bHeader, segmentHeaderPulses);

        /*Segment data*/
        addDataBlock(is, seg.data, pilotTonePulses);
    }

    private void generateTermHeader(int pilotTonePulses, InstructionStream is) throws Exception {
        int[] termHeader = new int[4];
        /*Termination header*/
        for (int i = 0; i < 4; i++) {
            termHeader[i] = 0;
        }
        addDataBlock(is, termHeader, pilotTonePulses);
    }

    private void addDataBlock(InstructionStream is, int[] data, int pilotPulses) throws Exception {

        /*Pilot tone*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(pilotPulses);

        /*Sync*/
        is.add(SignalGenerator.INSTR_SYNC);

        /*Data*/
        is.add(SignalGenerator.INSTR_DATA);
        is.add(data.length + 1);

        is.add(data);
        int chsum = data[0];
        for (int i = 1; i < data.length; i++) {
            chsum = chsum ^ data[i];
        }
        is.add(chsum);

        /*1 safety narrow pulse*/
        is.add(SignalGenerator.INSTR_NARROW);

        /*Block separator*/
        is.add(SignalGenerator.INSTR_BLOCKSEP);

    }

    private static final int[] lowerSilesiaTurbo2000BinaryLoaderData = {
        0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0x73, 0x22, 0x00, 0x20, 0xa7, 0x22, 0x4c, 0x2d, 0x09, 0xa9, 0x00, 0x8d, 0x87, 0x07,
        0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0x88, 0x8d, 0xe2, 0x02, 0x8d, 0xe0, 0x02, 0xa9, 0x07, 0x8d, 0xe1, 0x02,
        0x8d, 0xe3, 0x02, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x02, 0x85, 0x34, 0xa9, 0x04, 0x85, 0x33, 0x85, 0x35, 0x20, 0xc0, 0x07,
        0xb0, 0xd1, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0x29, 0x85, 0x32, 0xa9, 0x09, 0x85, 0x33, 0xa9, 0x2d, 0x85, 0x34, 0xa9, 0x09,
        0x85, 0x35, 0x20, 0xc0, 0x07, 0xb0, 0x3e, 0x20, 0x92, 0x07, 0xad, 0x29, 0x09, 0x85, 0x32, 0xad, 0x2a, 0x09, 0x85, 0x33,
        0xad, 0x2b, 0x09, 0x85, 0x34, 0xad, 0x2c, 0x09, 0x85, 0x35, 0x18, 0xa5, 0x34, 0x69, 0x01, 0x85, 0x34, 0xa5, 0x35, 0x69,
        0x00, 0x85, 0x35, 0xa9, 0x88, 0x8d, 0xe2, 0x02, 0xa9, 0x07, 0x8d, 0xe3, 0x02, 0x20, 0xc0, 0x07, 0xb0, 0x0b, 0x20, 0x84,
        0x07, 0x4c, 0x36, 0x07, 0x6c, 0xe2, 0x02, 0x00, 0x60, 0x20, 0x8e, 0xef, 0x20, 0x3e, 0xc6, 0x4c, 0x03, 0x07, 0xad, 0x29,
        0x09, 0xd0, 0x0f, 0xad, 0x2a, 0x09, 0xd0, 0x0a, 0xad, 0x2b, 0x09, 0xd0, 0x05, 0xad, 0x2c, 0x09, 0xf0, 0x17, 0xad, 0x87,
        0x07, 0xd0, 0xdd, 0xad, 0x29, 0x09, 0x8d, 0xe0, 0x02, 0xad, 0x2a, 0x09, 0x8d, 0xe1, 0x02, 0xee, 0x87, 0x07, 0x4c, 0x88,
        0x07, 0x6c, 0xe0, 0x02, 0xad, 0x0e, 0xd4, 0x8d, 0x27, 0x09, 0xa9, 0x34, 0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0x20, 0xd2,
        0x07, 0x60, 0xa5, 0x32, 0x85, 0xf0, 0xa5, 0x33, 0x85, 0xf1, 0x38, 0xa5, 0x34, 0xe5, 0x32, 0x8d, 0x22, 0x09, 0xa5, 0x35,
        0xe5, 0x33, 0x8d, 0x21, 0x09, 0x20, 0xbb, 0x08, 0x20, 0x8a, 0x08, 0xb0, 0xfb, 0xa9, 0xf8, 0x8d, 0x23, 0x09, 0xca, 0xd0,
        0xfd, 0x20, 0x84, 0x08, 0xb0, 0xee, 0xa2, 0x4a, 0x20, 0x84, 0x08, 0xb0, 0xe7, 0xe0, 0x62, 0x90, 0xe3, 0xee, 0x23, 0x09,
        0xd0, 0xf0, 0xa2, 0xa2, 0x20, 0x8a, 0x08, 0xb0, 0xd7, 0xe0, 0xb3, 0xb0, 0xf5, 0x20, 0x8a, 0x08, 0x90, 0x05, 0xa0, 0x8c,
        0x4c, 0xe7, 0x08, 0xa2, 0xbc, 0xa9, 0x01, 0x8d, 0x24, 0x09, 0x20, 0x84, 0x08, 0x90, 0x05, 0xa0, 0x8d, 0x4c, 0xe7, 0x08,
        0xe0, 0xd0, 0x2e, 0x24, 0x09, 0xa2, 0xbc, 0x90, 0xed, 0xad, 0x23, 0x09, 0x85, 0x80, 0x4d, 0x24, 0x09, 0x8d, 0x23, 0x09,
        0xad, 0x21, 0x09, 0x0d, 0x22, 0x09, 0xf0, 0x27, 0xad, 0x24, 0x09, 0x91, 0xf0, 0xa5, 0xf0, 0x18, 0x69, 0x01, 0x85, 0xf0,
        0xa5, 0xf1, 0x69, 0x00, 0x85, 0xf1, 0x38, 0xad, 0x22, 0x09, 0xe9, 0x01, 0x8d, 0x22, 0x09, 0xad, 0x21, 0x09, 0xe9, 0x00,
        0x8d, 0x21, 0x09, 0xa2, 0xbf, 0xd0, 0xae, 0xad, 0x23, 0x09, 0xf0, 0x04, 0xa0, 0x8f, 0xd0, 0x67, 0xa0, 0x01, 0xd0, 0x63,
        0x20, 0x8a, 0x08, 0x90, 0x01, 0x60, 0xad, 0x0f, 0xd2, 0x29, 0x10, 0x4a, 0x09, 0x10, 0x8d, 0x01, 0xd2, 0xa0, 0x14, 0x88,
        0xd0, 0xfd, 0x38, 0xe8, 0xf0, 0xeb, 0xad, 0x0f, 0xd2, 0x4d, 0x25, 0x09, 0x29, 0x10, 0xf0, 0xf3, 0xad, 0x0f, 0xd2, 0x8d,
        0x25, 0x09, 0x18, 0xad, 0x26, 0x09, 0x49, 0x0f, 0x8d, 0x26, 0x09, 0x8d, 0x1a, 0xd0, 0x60, 0xba, 0x8e, 0x28, 0x09, 0xad,
        0x36, 0x02, 0x8d, 0x1f, 0x09, 0xea, 0xea, 0xad, 0x37, 0x02, 0x8d, 0x20, 0x09, 0xea, 0xea, 0xa9, 0xd9, 0x8d, 0x36, 0x02,
        0xa9, 0x08, 0x8d, 0x37, 0x02, 0xa9, 0x00, 0x8d, 0x0e, 0xd4, 0x8d, 0x00, 0xd4, 0xa9, 0x80, 0x8d, 0x0e, 0xd2, 0x60, 0x8c,
        0x03, 0x03, 0xad, 0x1f, 0x09, 0x8d, 0x36, 0x02, 0xad, 0x20, 0x09, 0x8d, 0x37, 0x02, 0xad, 0x2f, 0x02, 0x8d, 0x00, 0xd4,
        0xad, 0x27, 0x09, 0x8d, 0x0e, 0xd4, 0xae, 0x28, 0x09, 0xe8, 0xe8, 0x9a, 0x58, 0xa5, 0x10, 0x8d, 0x0e, 0xd2, 0xa9, 0x3c,
        0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0xac, 0x03, 0x03, 0x18, 0x30, 0x01, 0x60, 0x38, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0xff,
        0x8d, 0x01, 0xd3, 0x8d, 0xf8, 0x03, 0xa9, 0x49, 0x85, 0x0c, 0xa9, 0x09, 0x85, 0x0d, 0x20, 0x74, 0xe4, 0xa9, 0x53, 0x85,
        0x0a, 0xa9, 0x09, 0x85, 0x0b, 0x18, 0x60, 0xa2, 0x65, 0xa0, 0x09, 0x20, 0x42, 0xc6, 0xa9, 0x01, 0x8d, 0xfc, 0x02, 0x20,
        0xfc, 0xfd, 0x4c, 0x03, 0x07, 0x7d, 0x4c, 0x53, 0x54, 0x32, 0x30, 0x30, 0x30, 0x42, 0x4c, 0x20, 0x9b, 0x71, 0x02, 0xa9,
        0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x00, 0x85, 0x34, 0xa9, 0x07, 0x85, 0x35, 0xa0, 0x00, 0xae, 0x72, 0x22,
        0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6, 0x33, 0xe6, 0x35, 0xca, 0xd0, 0xf2, 0xae, 0x71, 0x22, 0xf0,
        0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c, 0x00, 0x07

    };

}
