package turgen.plugin.csturbo;

import turgen.plugin.*;
import turgen.generator.SignalGenerator;
import turgen.config.Configuration;
import turgen.*;
import java.io.*;
import javax.swing.*;
import turgen.config.BooleanConfigEntry;
import turgen.config.ConfigClass;

import turgen.config.IntegerConfigEntry;
import turgen.dtb.*;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.*;

public class Turbo2000Plugin implements Plugin {

    private CsTurboPluginsPanel panel;
    private final int[] casChunkName = {116, 114, 50, 107};

    private ConfigClass cfcTurbo2000;
    private IntegerConfigEntry cfgPilotLenA;
    private IntegerConfigEntry cfgPilotLenB;
    private IntegerConfigEntry cfgPilotLenBlockLoading;
    private BooleanConfigEntry cfgPrependLoader;

    /**
     * Nova instance SuperTurboPlugin
     */
    public Turbo2000Plugin() {

    }

    /**
     * Identifikacni jmeno modulu
     */
    public String getName() {
        return "Turbo 2000";

    }

    /**
     * **********************************************************
     */
    /*Tvorba instrukci pro generator*/
    public int[] convertItem(AbstractPlayListItem _pl) throws Exception {

        /*Get CsTurboPlaylistItem*/
        CsTurboPlaylistItem pl = (CsTurboPlaylistItem) _pl;

        /*Pripraven vystupni proud pro instrukce*/
        InstructionStream is = new InstructionStream();

        /*If loader is to be prepended, then we prepend it*/
        if (cfgPrependLoader.getAsBoolean() == true) {
            is.add(LoaderBinaryData.universalTurboLoader);
        }


        /*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 CsTurboPlaylistItem.CV_BINTURBO:
            case CsTurboPlaylistItem.CV_BASIC:
            case CsTurboPlaylistItem.CV_DATA: {
                convertFullFile(is, pl);
                break;
            }
            /*Monoliticky binarni soubor*/
            case CsTurboPlaylistItem.CV_MONOLITHIC: {
                convertMonolithicBinary(is, pl);
                break;
            }
            /*Specialni retezove zavadeni*/
            case CsTurboPlaylistItem.CV_SEGMENTED_CHAIN: {
                convertChainLoading(is, pl);
                break;
            }
            /*Specialni blokove zavadeni*/
            case CsTurboPlaylistItem.CV_SEGMENTED_BLOCK: {
                convertBlockLoading(is, pl);
                break;
            }
        }

        is.add(SignalGenerator.INSTR_END);

        /*Instrukce pro generatory*/
        return is.getInstructions();

    }

    public AbstractPlayListItem getItemFromPanel() {
        CsTurboPlaylistItem 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 CsTurboPluginsPanel(false);

        /*Initialize configuration class*/
        cfcTurbo2000 = new ConfigClass("Turbo2000", "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);
        cfgPilotLenBlockLoading = new IntegerConfigEntry("pilotLenBlockLoading", "Pilot tone length for BlockLoading [pulse]", 2048, 256, 8192);
        cfgPrependLoader = new BooleanConfigEntry("prependLoader", "Prepend Universal Turbo loader", false);

        /*Add entries to the class*/
        cfcTurbo2000.addEntry(cfgPilotLenA);
        cfcTurbo2000.addEntry(cfgPilotLenB);
        cfcTurbo2000.addEntry(cfgPilotLenBlockLoading);
        cfcTurbo2000.addEntry(cfgPrependLoader);

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

    }

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

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

    private void convertFullFile(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

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

        int hdrLoad = Integer.parseInt(cpi.load);
        int hdrRun = Integer.parseInt(cpi.run);
        int hdrLength = Integer.parseInt(cpi.length);
        int hdrType = Integer.parseInt(cpi.type);

        /*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(cpi.fname, hdrType, hdrLoad, hdrLength, hdrRun);

        is.add(hdr);

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

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

        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 void convertMonolithicBinary(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

        int hdrLoad = Integer.parseInt(cpi.load);
        int hdrRun = Integer.parseInt(cpi.run);
        int hdrLength = Integer.parseInt(cpi.length);
        int hdrType = Integer.parseInt(cpi.type);

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

        /*HLAVICKA*/
        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(cpi.fname, hdrType, hdrLoad, hdrLength, hdrRun);

        is.add(hdr);

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

        /*DATA*/
        DOS2Binary dtb = new DOS2Binary(cpi.inFile);
        dtb.analyzeFromFile();

        int[] fileData = dtb.getFirstDataSegment().data;

        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 pluse + safety pause*/
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_BLOCKSEP);
    }

    private void convertChainLoading(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

        int currentPause = 0;
        int plen = cpi.silenceList.length;

        File f = null;

        /*Nejprve se vytvori instance DOS2Binary*/
        DOS2Binary ae = new DOS2Binary(cpi.inFile);
        ae.analyzeFromFile();
        ae.prepareForDumbLoader();

        /*Mirne navyseni poctu zavadecich pulzu*/
        int currentPilotLenA = cfgPilotLenA.getAsInt();
        int currentPilotLenB = cfgPilotLenB.getAsInt();

        /*CHAINLOADER*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(currentPilotLenA);
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_DATA);
        is.add(19);

        /*Adresy*/
        int ls = 0;
        int ll = 0;
        int lr = 0;
        lr = chainLoader_run;
        ll = chainLoader_data.length;
        ls = chainLoader_load;

        int[] header = Utils.createT2KHeader(cpi.fname, 3, ls, ll, lr);

        /*Zapsat do proudu*/
        is.add(header);


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

        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(currentPilotLenB);
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_DATA);
        is.add(ll + 2);

        /*Data zavadece*/
        int p = 255;
        is.add(255);

        for (int k = 0; k < ll; k++) {

            p = p ^ chainLoader_data[k];
        }
        is.add(chainLoader_data);
        /*Kontroni soucet*/
        is.add(p);

        /*Safety pulse + pause*/
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(3);

        /*Instrukce pro vygenerovani zavadece jsou jiz hotove*/
        int currentSegment = 0;
        int realCounter = 0;

        boolean initTag = false;

        while (currentSegment < ae.allSegments.size()) {

            /*Vezmeme segment*/
            Segment sct = (Segment) ae.allSegments.get(currentSegment);
            initTag = false;

            /*Zavadeci tony*/
            is.add(SignalGenerator.INSTR_PILOTTONE);
            is.add(currentPilotLenA);
            is.add(SignalGenerator.INSTR_NARROW);
            is.add(SignalGenerator.INSTR_DATA);
            is.add(19);


            /*Do hlavicky radeji cislo segmentu*/
            String s1 = Integer.toString(currentSegment);
            String s2 = Integer.toString(ae.allSegments.size() - 1);
            s1 = " " + s1 + "/" + s2;
            if (s1.length() > 8) {
                s1 = s1.substring(0, 8);
            }
            header = Utils.createT2KHeader(s1, 3, 0, 0, 0);

            int progress = 1;

            /*Podle typu segmentu*/
            switch (sct.type) {
                /*Datovy segment*/
                case (Segment.DATA_SEGMENT): {

                    int mark = 68;
                    lr = sct.start;
                    /*Neni-li to posledni segment*/
                    if (currentSegment < ae.allSegments.size() - 1) {

                        /*Podivat se jestli nenasleduje INIT nebo RUN*/
                        Segment js = (Segment) ae.allSegments.get(currentSegment + 1);
                        if (js.type == Segment.RUN_SEGMENT) {
                            lr = js.jump;
                            mark = 88;
                            progress = 2;
                            if (currentSegment == ae.allSegments.size() - 2) {
                                mark += 128;
                            }
                        }
                        if (js.type == Segment.INIT_SEGMENT) {
                            lr = js.jump;
                            mark = 89;
                            progress = 2;
                            initTag = true;
                            if (currentSegment == ae.allSegments.size() - 2) {
                                mark += 128;
                            }
                        }
                    }
                    /*Je-li posledni, pak typ segmentu bude oznacen inverzne*/
                    if (currentSegment == ae.allSegments.size() - 1) {
                        mark += 128;
                    }

                    header[2] = mark;
                    ls = sct.start;
                    ll = sct.data.length;

                    header[11] = realCounter;
                    realCounter++;

                    header[12] = ls % 256;
                    header[13] = ls / 256;
                    header[14] = ll % 256;
                    header[15] = ll / 256;
                    header[16] = lr % 256;
                    header[17] = lr / 256;
                    p = 0;
                    for (int i = 1; i < 18; i++) {
                        p = p ^ header[i];
                    }
                    header[18] = p;

                    is.add(header);

                    break;
                }
                /*INIT segment*/
                case (Segment.INIT_SEGMENT): {
                    int mark = 73;
                    /*Je-li posledni, pak se typ segmentu bude oznacen inverzne*/
                    if (currentSegment == ae.allSegments.size() - 1) {
                        mark += 128;
                    }

                    header[2] = mark;
                    ls = 738;
                    ll = 2;
                    lr = sct.jump;
                    header[11] = realCounter;
                    realCounter++;
                    header[12] = ls % 256;
                    header[13] = ls / 256;
                    header[14] = ll % 256;
                    header[15] = ll / 256;
                    header[16] = lr % 256;
                    header[17] = lr / 256;
                    p = 0;
                    for (int i = 1; i < 18; i++) {
                        p = p ^ header[i];
                    }
                    header[18] = p;
                    is.add(header);

                    initTag = true;
                    break;
                }
                /*RUN segment*/
                case (Segment.RUN_SEGMENT): {

                    int mark = 82;
                    /*Je-li posledni, pak se typ segmentu bude oznacen inverzne*/
                    if (currentSegment == ae.allSegments.size() - 1) {
                        mark += 128;
                    }

                    header[2] = mark;
                    ls = 736;
                    ll = 2;
                    lr = sct.jump;
                    header[11] = realCounter;
                    realCounter++;
                    header[12] = ls % 256;
                    header[13] = ls / 256;
                    header[14] = ll % 256;
                    header[15] = ll / 256;
                    header[16] = lr % 256;
                    header[17] = lr / 256;
                    p = 0;
                    for (int i = 1; i < 18; i++) {
                        p = p ^ header[i];
                    }
                    header[18] = p;
                    is.add(header);

                    break;
                }
                case (Segment.RUNINIT_SEGMENT): {
                    int mark = 75;
                    /*Je-li posledni, pak se typ segmentu bude oznacen inverzne*/
                    if (currentSegment == ae.allSegments.size() - 1) {
                        mark += 128;
                    }

                    header[2] = mark;
                    ls = 736;
                    ll = 4;
                    lr = 736;

                    header[11] = realCounter;
                    realCounter++;
                    header[12] = ls % 256;
                    header[13] = ls / 256;
                    header[14] = ll % 256;
                    header[15] = ll / 256;
                    header[16] = lr % 256;
                    header[17] = lr / 256;
                    p = 0;
                    for (int i = 1; i < 18; i++) {
                        p = p ^ header[i];
                    }
                    header[18] = p;

                    is.add(header);

                    break;
                }
            }

            /*RealCounter nesmi prekrocit 255*/
            if (realCounter == 255) {
                throw new DOS2BinaryProcessingException("Chainloader cannot process binary file with more than 254 segments");
            }

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

            /*Hlavicka je zapsana*/
            /*Po hlavicce*/
            is.add(SignalGenerator.INSTR_PILOTTONE);
            is.add(currentPilotLenB);
            is.add(SignalGenerator.INSTR_NARROW);
            is.add(SignalGenerator.INSTR_DATA);

            /*Opet podle segmentu*/
            switch (sct.type) {

                /*Datovy segment*/
                case (Segment.DATA_SEGMENT): {
                    p = 255;

                    is.add(ll + 2);

                    is.add(255);
                    for (int k = 0; k < ll; k++) {
                        p = p ^ sct.data[k];
                    }
                    is.add(sct.data);
                    /*Kontroni soucet*/
                    is.add(p);
                    break;
                }

                /*RUNINIT segment*/
                case (Segment.RUNINIT_SEGMENT): {
                    p = 255;
                    is.add(6);
                    is.add(p);
                    is.add(sct.jump % 256);
                    p = p ^ (sct.jump % 256);
                    is.add(sct.jump / 256);
                    p = p ^ (sct.jump / 256);
                    is.add(sct.jump2 % 256);
                    p = p ^ (sct.jump2 % 256);
                    is.add(sct.jump2 / 256);
                    p = p ^ (sct.jump2 / 256);
                    is.add(p);
                    break;
                }

                /*INIT segment*/
                case (Segment.INIT_SEGMENT): {
                    p = 255;
                    is.add(4);
                    is.add(p);
                    is.add(sct.jump % 256);
                    p = p ^ (sct.jump % 256);
                    is.add(sct.jump / 256);
                    p = p ^ (sct.jump / 256);
                    is.add(p);
                    break;
                }

                case (Segment.RUN_SEGMENT): {
                    p = 255;
                    is.add(4);
                    is.add(p);
                    is.add(sct.jump % 256);
                    p = p ^ (sct.jump % 256);
                    is.add(sct.jump / 256);
                    p = p ^ (sct.jump / 256);
                    is.add(p);
                    break;
                }
            }

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


            /*Mozna prestavka za INIT*/
            if (initTag == true) {
                if (currentPause < plen) {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(cpi.silenceList[currentPause]);
                    currentPause++;
                }
            }

            currentSegment += progress;

        } /*Konec while*/

    }

    private void convertBlockLoading(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

        int currentPause = 0;
        int plen = cpi.silenceList.length;

        File f = null;


        /*Nejprve se vytvori instance DOS2Binary*/
        DOS2Binary ae = new DOS2Binary(cpi.inFile);
        ae.analyzeFromFile();
        ae.prepareForDumbLoader();


        /*Loader*/
        int plenA = cfgPilotLenA.getAsInt();
        int plenB = cfgPilotLenB.getAsInt();
        int plenK = cfgPilotLenBlockLoading.getAsInt();

        /*Tvorba instrukci pro loader*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(plenA);
        is.add(SignalGenerator.INSTR_SYNC);
        is.add(SignalGenerator.INSTR_DATA);
        is.add(19);


        /*Adresy*/
        int ls = 0;
        int ll = 0;
        int lr = 0;
        lr = blockLoader_run;
        ll = blockLoader_data.length;
        ls = blockLoader_load;

        int[] header = Utils.createT2KHeader(cpi.fname, 3, ls, ll, lr);


        /*Zapsat do souboru*/
        is.add(header);


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

        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(plenB);
        is.add(SignalGenerator.INSTR_SYNC);
        is.add(SignalGenerator.INSTR_DATA);
        is.add(ll + 2);

        /*Data zavadece*/
        int p = 255;
        is.add(255);

        /*Vytvori se kopie*/
        int[] bl_data = new int[blockLoader_data.length];
        for (int i = 0; i < bl_data.length; i++) {
            bl_data[i] = blockLoader_data[i];
        }

        /*Tabulka segmentu*/

        /*Tvorba tabulky, ne vic nez 62 segmentu*/
        int sl = ae.allSegments.size();
        if (sl > 62) {
            throw new DOS2BinaryProcessingException("Blockloader cannot process binary file with more than 62 segments");
        }

        Segment s;
        int index = blockLoader_table_offset;
        bl_data[blockLoader_counter_offset] = sl * 4;

        for (int i = 0; i < sl; i++) {
            s = (Segment) ae.allSegments.get(i);
            bl_data[index] = (s.start) % 256;
            bl_data[index + 1] = (s.start) / 256;
            bl_data[index + 2] = (s.stop + 1) % 256;
            bl_data[index + 3] = (s.stop + 1) / 256;
            index += 4;
        }
        for (int k = 0; k < ll; k++) {
            p = p ^ bl_data[k];
        }
        is.add(bl_data);
        /*Kontroni soucet*/
        is.add(p);
        is.add(SignalGenerator.INSTR_NARROW);

        /*Pauza 3 sekundy*/
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(3);

        int currentSegment = 0;

        while (currentSegment < ae.allSegments.size()) {

            /*Vezmeme segment*/
            Segment sct = (Segment) ae.allSegments.get(currentSegment);

            is.add(SignalGenerator.INSTR_PILOTTONE);
            is.add(plenK);
            is.add(SignalGenerator.INSTR_SYNC);
            is.add(SignalGenerator.INSTR_DATA);

            p = 0;

            /*Mnozstvi dat: delka segmentu + 2 bajty, zacina se 255*/
            is.add(sct.stop - sct.start + 1 + 2);
            is.add(0);
            for (int k = 0; k < sct.data.length; k++) {
                p = p ^ sct.data[k];
            }
            is.add(sct.data);

            /*Kontroni soucet*/
            is.add(p);

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

            /*Mozna prestavka za INIT*/
            if (sct.type == Segment.INIT_SEGMENT || sct.type == Segment.RUNINIT_SEGMENT) {
                if (currentPause < plen) {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(cpi.silenceList[currentPause]);
                    currentPause++;
                }
            }

            currentSegment++;
        } /*Konec while*/


    }

    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[]{"tr2k"};
    }

    /**
     * ***********************************************************************
     */
    /**
     * ***********************************************************************
     */
    static int chainLoader_load = 1800;
    static int chainLoader_run = 1800;
    static int[] chainLoader_data = {0x20, 0x9f, 0x08, 0x4c, 0x16, 0x07, 0xa2, 0xff, 0x8e, 0xfc, 0x02, 0x20, 0xfc, 0xfd, 0xa9, 0x08, 0x85, 0x33, 0x85, 0x35, 0xa9, 0x8c, 0x85, 0x34, 0xa9, 0x7b, 0x85, 0x32, 0xa9, 0x00, 0x20, 0xab, 0x07,
        0x90, 0xeb, 0x18, 0xad, 0x85, 0x08, 0xcd, 0x7a, 0x08, 0xf0, 0x05, 0xa9, 0x01, 0x4c, 0x0e, 0x07, 0xad, 0x86, 0x08, 0x85, 0x32, 0x18, 0x6d, 0x88, 0x08, 0x85, 0x34, 0xad, 0x87, 0x08, 0x85, 0x33,
        0x6d, 0x89, 0x08, 0x85, 0x35, 0xa9, 0xff, 0x20, 0xab, 0x07, 0xb0, 0x05, 0xa9, 0x04, 0x4c, 0x0e, 0x07, 0xea, 0xad, 0x7c, 0x08, 0x29, 0x7f, 0xc9, 0x52, 0xf0, 0x37, 0xc9, 0x44, 0xf0, 0x33, 0xc9,
        0x49, 0xf0, 0x26, 0xc9, 0x58, 0xf0, 0x07, 0xc9, 0x59, 0xf0, 0x12, 0x4c, 0x92, 0x07, 0xad, 0x8a, 0x08, 0x8d, 0xe0, 0x02, 0xad, 0x8b, 0x08, 0x8d, 0xe1, 0x02, 0x4c, 0x9b, 0x07, 0xad, 0x8a, 0x08,
        0x8d, 0xe2, 0x02, 0xad, 0x8b, 0x08, 0x8d, 0xe3, 0x02, 0x20, 0x98, 0x07, 0x4c, 0x9b, 0x07, 0x6c, 0xe2, 0x02, 0xad, 0x7c, 0x08, 0x29, 0x80, 0xf0, 0x03, 0x6c, 0xe0, 0x02, 0xee, 0x7a, 0x08, 0x4c,
        0x16, 0x07, 0x85, 0x36, 0xa9, 0x34, 0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0xa9, 0x80, 0x85, 0x10, 0x8d, 0x0e, 0xd2, 0x18, 0xa0, 0x00, 0x84, 0x30, 0x84, 0x31, 0x8c, 0x0e, 0xd4, 0x8c, 0x00, 0xd4,
        0x08, 0xd0, 0x70, 0x20, 0x55, 0x08, 0x90, 0xf9, 0xa9, 0x00, 0x85, 0x2e, 0x85, 0x37, 0xa0, 0xb4, 0x20, 0x50, 0x08, 0x90, 0xec, 0xc0, 0xd8, 0x90, 0xea, 0xe6, 0x2e, 0xd0, 0xf1, 0xc6, 0x37, 0xa0,
        0xd1, 0x20, 0x55, 0x08, 0x90, 0xdb, 0xc0, 0xde, 0xb0, 0xf5, 0x20, 0x55, 0x08, 0x90, 0x44, 0xa0, 0xc6, 0x4c, 0x17, 0x08, 0x28, 0xd0, 0x08, 0xa5, 0x36, 0x45, 0x2f, 0xd0, 0x37, 0xf0, 0x0c, 0xa0,
        0x00, 0xa5, 0x2f, 0x91, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xa0, 0xc8, 0x08, 0xa9, 0x01, 0x85, 0x2f, 0x20, 0x50, 0x08, 0x90, 0x1c, 0xc0, 0xe3, 0x26, 0x2f, 0xa0, 0xc6, 0x90, 0xf3, 0xa5,
        0x31, 0x45, 0x2f, 0x85, 0x31, 0xa5, 0x32, 0xc5, 0x34, 0xa5, 0x33, 0xe5, 0x35, 0x90, 0xc5, 0xa9, 0x00, 0xc5, 0x31, 0x68, 0xa9, 0xc0, 0x8d, 0x0e, 0xd4, 0x85, 0x10, 0x8d, 0x0e, 0xd2, 0xa9, 0x3c,
        0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0x60, 0x20, 0x55, 0x08, 0x90, 0x24, 0xa2, 0x04, 0xca, 0xd0, 0xfd, 0xa5, 0x30, 0x4a, 0x25, 0x37, 0x8d, 0x1a, 0xd0, 0xc8, 0xf0, 0x13, 0xa5, 0x11, 0xf0, 0x0d,
        0xad, 0x0f, 0xd2, 0x29, 0x10, 0xc5, 0x30, 0xf0, 0xf0, 0x85, 0x30, 0x38, 0x60, 0xc6, 0x11, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x53, 0x20, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x52, 0x9b, 0xa2, 0x90, 0xa0, 0x08, 0x20, 0x42, 0xc6, 0xa9, 0x01, 0x8d,
        0xfc, 0x02, 0x20, 0xfc, 0xfd, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0x60};

    private static final int blockLoader_run = 2048;
    private static final int blockLoader_load = 2048;
    private static final int blockLoader_counter_offset = 0x143;
    private static final int blockLoader_table_offset = 0x144;

    private static final int[] blockLoader_data = {0x4c, 0xd2, 0x08, 0x85, 0x36, 0xa9, 0x34, 0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0xa9, 0x80, 0x85, 0x10, 0x8d, 0x0e, 0xd2, 0x18, 0xa0, 0x00, 0x84, 0x30, 0x84, 0x31, 0x8c, 0x0e, 0xd4, 0x8c, 0x00, 0xd4,
        0x08, 0xd0, 0x70, 0x20, 0xad, 0x08, 0x90, 0xf9, 0xa9, 0x00, 0x85, 0x2e, 0x85, 0x37, 0xa0, 0xb4, 0x20, 0xa8, 0x08, 0x90, 0xec, 0xc0, 0xd8, 0x90, 0xea, 0xe6, 0x2e, 0xd0, 0xf1, 0xc6, 0x37, 0xa0,
        0xd1, 0x20, 0xad, 0x08, 0x90, 0xdb, 0xc0, 0xde, 0xb0, 0xf5, 0x20, 0xad, 0x08, 0x90, 0x44, 0xa0, 0xc6, 0x4c, 0x6f, 0x08, 0x28, 0xd0, 0x08, 0xa5, 0x36, 0x45, 0x2f, 0xd0, 0x37, 0xf0, 0x0c, 0xa0,
        0x00, 0xa5, 0x2f, 0x91, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xa0, 0xc8, 0x08, 0xa9, 0x01, 0x85, 0x2f, 0x20, 0xa8, 0x08, 0x90, 0x1c, 0xc0, 0xe3, 0x26, 0x2f, 0xa0, 0xc6, 0x90, 0xf3, 0xa5,
        0x31, 0x45, 0x2f, 0x85, 0x31, 0xa5, 0x32, 0xc5, 0x34, 0xa5, 0x33, 0xe5, 0x35, 0x90, 0xc5, 0xa9, 0x00, 0xc5, 0x31, 0x68, 0xa9, 0xc0, 0x8d, 0x0e, 0xd4, 0x85, 0x10, 0x8d, 0x0e, 0xd2, 0xa9, 0x3c,
        0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0x60, 0x20, 0xad, 0x08, 0x90, 0x24, 0xa2, 0x04, 0xca, 0xd0, 0xfd, 0xa5, 0x30, 0x4a, 0x25, 0x37, 0x8d, 0x1a, 0xd0, 0xc8, 0xf0, 0x13, 0xa5, 0x11, 0xf0, 0x0d,
        0xad, 0x0f, 0xd2, 0x29, 0x10, 0xc5, 0x30, 0xf0, 0xf0, 0x85, 0x30, 0x38, 0x60, 0xc6, 0x11, 0x18, 0x60, 0x20, 0x53, 0x0a, 0xa9, 0x00, 0x8d, 0x40, 0x09, 0xa2, 0x0c, 0x8e, 0xfc, 0x02, 0xa9, 0x01,
        0x20, 0xfc, 0xfd, 0xad, 0x40, 0x09, 0xcd, 0x43, 0x09, 0xd0, 0x03, 0x6c, 0xe0, 0x02, 0xa9, 0x41, 0x8d, 0xe2, 0x02, 0xa9, 0x09, 0x8d, 0xe3, 0x02, 0xac, 0x40, 0x09, 0xb9, 0x44, 0x09, 0x85, 0x32,
        0xc8, 0xb9, 0x44, 0x09, 0x85, 0x33, 0xc8, 0xb9, 0x44, 0x09, 0x85, 0x34, 0xc8, 0xb9, 0x44, 0x09, 0x85, 0x35, 0xa9, 0x00, 0xa2, 0x00, 0xa0, 0x00, 0x20, 0x03, 0x08, 0xb0, 0x0d, 0xa9, 0xff, 0x8d,
        0xfc, 0x02, 0xa9, 0x04, 0x20, 0xfc, 0xfd, 0x4c, 0xd2, 0x08, 0x20, 0x3d, 0x09, 0xee, 0x40, 0x09, 0xee, 0x40, 0x09, 0xee, 0x40, 0x09, 0xee, 0x40, 0x09, 0x4c, 0xe4, 0x08, 0x6c, 0xe2, 0x02, 0x00,
        0x60, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x54, 0x53, 0x20, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x52, 0x9b, 0xa2, 0x44, 0xa0, 0x0a, 0x20, 0x42, 0xc6, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01,
        0x85, 0x09, 0x60};

    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[3];
        MonolithicBinaryConvertor mbc = new MonolithicBinaryConvertor(false, this);
        ChainloadingBinaryConvertor cbc = new ChainloadingBinaryConvertor(false, 1800, 2191, this);
        BlockloadingBinaryConvertor bbc = new BlockloadingBinaryConvertor(false, 2048, 2627, this);
        bc[0] = mbc;
        bc[1] = cbc;
        bc[2] = bbc;
        return bc;
    }

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

    public boolean isUsingAlternateFileChooser() {
        return false;
    }

}
