package turgen.plugin.plturbo;

import turgen.plugin.*;
import turgen.generator.SignalGenerator;
import turgen.config.Configuration;
import turgen.*;
import java.io.*;
import java.util.Arrays;
import javax.swing.*;
import turgen.config.BooleanConfigEntry;
import turgen.config.ConfigClass;
import turgen.config.IntegerConfigEntry;
import turgen.config.PulseCorrectionConfigEntry;
import turgen.dtb.DOS2Binary;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.DataChunk;
import turgen.tapeimage.TapeImageChunk;
import turgen.tapeimage.TurboChunk;

public class PlKSOTurbo2000Plugin implements Plugin {

    private PlTurboPluginPanel panel;
    private InOutProvider ioProvider;
    private final int[] casChunkName = {116, 114, 107, 115};

    private ConfigClass cfcPlKSOTurbo;
    private BooleanConfigEntry cfgInvertPolarity;
    private IntegerConfigEntry cfgPilotLenA;
    private IntegerConfigEntry cfgPilotLenBlock;
    private IntegerConfigEntry cfgSilenceAfterHeader;
    private BooleanConfigEntry cfgPauseAfterHeader;
    private PulseCorrectionConfigEntry cfgPulseCorrections;
    

    /**
     * Nova instance PlKSOTurbo2000Plugin
     */
    public PlKSOTurbo2000Plugin() {
    }

    /**
     * Jake rychlosti jsou podporovany
     */
    /**
     * Identifikacni jmeno zasuvneho modulu
     */
    public String getName() {
        return "KSO Turbo 2000";
    }

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

        
        RandomAccessFile raf = null;

        SimplePlaylistItem pl;
        pl = (SimplePlaylistItem) _pl;
        
        pl.pulses.applyCorrections(cfgPulseCorrections.getAsPulseCrate(), false);
        
        try {

            /*Otevreni vsech souboru a docasnych proudu*/
            raf = new RandomAccessFile(pl.inFile, "r");
            int l = (int) raf.length();

            if (l > 1024 * 1024) {
                throw new FileTooLargeException(pl.inFile);
            }

            /*Read all data from file and close it*/
            byte[] fData = new byte[l];
            int[] fileData = new int[l];

            raf.readFully(fData);
            raf.close();

            for (int i = 0; i < fData.length; i++) {
                fileData[i] = fData[i] < 0 ? fData[i] + 256 : fData[i];
            }

            /*Check for binary file*/
            int[] initLocations;
            try {
                DOS2Binary dtb = new DOS2Binary(pl.inFile);
                dtb.analyzeFromData(fileData);
                initLocations = dtb.getInitLocations();
            } catch (Exception e) {
                initLocations = new int[0];
            }

            InstructionStream is = new InstructionStream();

            /*Nastaveni*/
            Utils.addSetupInstruction(is, pl.pulses, casChunkName, 1 - cfgInvertPolarity.getAsInt(), SignalGenerator.FLAG_ORDER_HL);

            /*Hlavicka, zavadeci ton, 10 znaku nazev, suma vznikla scitanim*/
            is.add(SignalGenerator.INSTR_PILOTTONE);
            is.add(cfgPilotLenA.getAsInt());
            is.add(SignalGenerator.INSTR_DATA);
            is.add(13);

            int[] header = new int[12];
            /*Pocatek hlavicky*/
            header[0] = 0;
            header[1] = 255;

            /*Vymezerovat nazev*/
            for (int i = 2; i < 12; i++) {
                header[i] = 32;
            }

            int max = 10;
            int hl = pl.fname.length();
            if (hl > max) {
                hl = max;
            }

            /*Jmeno souboru*/
            for (int i = 2; i < 12; i++) {
                if ((i - 2) < hl) {
                    header[i] = (byte) pl.fname.charAt(i - 2);
                }
                else {
                    header[i] = 32;
                }
            }

            /*Kontrolni soucet*/
            int chsum = 0;
            for (int i = 0; i < 12; i++) {
                chsum += header[i];

            }
            is.add(header);
            is.add(chsum % 256);
            is.add(SignalGenerator.INSTR_STOP); /*Was narrow*/

            /*Silence after header*/
            addSilenceAfterHeader(is);
      

            /*ZAPIS JEDNOTLIVYCH BLOKU*/
            int[] block = new int[3072 + 2];
            int fptr = 0;

            int rest = 0;

            int startPtr;
            int endPtr;

            /*Pokud neni vse zapsano*/
            while (fptr < l) {

                startPtr = fptr;

                /*Zapsat zavadeci ton*/
                is.add(SignalGenerator.INSTR_PILOTTONE);
                is.add(cfgPilotLenBlock.getAsInt());
                is.add(SignalGenerator.INSTR_DATA);
                is.add(3072 + 2 + 1);

                /*Kolik zbyva do konce a je-li to vice nez 3072 bytu*/
                rest = l - fptr;
                if (rest > 3072) {
                    rest = 3072;
                }

                /*Delka bloku*/
                block[0] = rest % 256;
                block[1] = rest / 256;

                /*Data bloku*/
                for (int i = 0; i < rest; i++) {
                    block[i + 2] = fileData[fptr];
                    fptr++;
                }

                /*Doplnit nulami*/
                for (int i = rest; i < 3072; i++) {
                    block[i + 2] = 0;
                }

                /*Kontrolni soucet*/
                chsum = 0;
                for (int i = 0; i < 3074; i++) {
                    chsum += block[i];
                }
                is.add(block);
                is.add(chsum % 256);

                endPtr = fptr;

                /*Safety pulse*/
                is.add(SignalGenerator.INSTR_STOP); /*Was narrow*/

                /*Silence after init or simple block separation*/
                int wait = Utils.calculateInitSilence(startPtr, endPtr - 1, initLocations, pl.silenceList);
                if (wait == 0) {
                    is.add(SignalGenerator.INSTR_BLOCKSEP);
                }
                else {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(wait);
                }

            }
            /*Byl-li posledni blok dlouhy 3072 bytu, tak je potreba zapsat
             *EOF blok
             */
            if (rest == 3072) {
                is.add(SignalGenerator.INSTR_PILOTTONE);
                is.add(cfgPilotLenBlock.getAsInt());
                is.add(SignalGenerator.INSTR_DATA);
                is.add(3072 + 2 + 1);
                Arrays.fill(block, 0, block.length, 0);
                is.add(block);

            }
            /*Safety pulse + safety pause*/
            is.add(SignalGenerator.INSTR_STOP); /*Was narrow*/
            is.add(SignalGenerator.INSTR_BLOCKSEP);

            is.add(SignalGenerator.INSTR_END);
            return is.getInstructions();

        } catch (Exception e) {
            throw e;

        } finally {
            try {
                if (raf != null) {
                    raf.close();
                }
            } catch (IOException e) {
                /*No reactions*/
            }
        }

    }

    public AbstractPlayListItem getItemFromPanel() {
        SimplePlaylistItem 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 PlTurboPluginPanel(10);

        /*Initialize configuration class*/
        cfcPlKSOTurbo = new ConfigClass("PlKSOTurbo", "KSO Turbo 2000");

        /*Initialize configuration entries*/
        cfgInvertPolarity = new BooleanConfigEntry("invertPolarity", "Invert polarity of pulses", false);
        cfgPilotLenA = new IntegerConfigEntry("pilotLenA", "Header block pilot tone length [pulse]", 3072, 256, 8192);
        cfgPilotLenBlock = new IntegerConfigEntry("pilotLenBlock", "Data block pilot tone length [pulse]", 2048, 256, 8192);
        cfgSilenceAfterHeader = new IntegerConfigEntry("silenceAfterHeader","Silence after header [s]",0,0,30);
        cfgPauseAfterHeader = new BooleanConfigEntry("pauseAfterHeader","Pause after header",false);
        cfgPulseCorrections = new PulseCorrectionConfigEntry("pulseCorrections", "Pulse corrections");

        /*Add entries to the class*/
        cfcPlKSOTurbo.addEntry(cfgInvertPolarity);
        cfcPlKSOTurbo.addEntry(cfgPilotLenA);
        cfcPlKSOTurbo.addEntry(cfgPilotLenBlock);
        cfcPlKSOTurbo.addEntry(cfgSilenceAfterHeader);
        cfcPlKSOTurbo.addEntry(cfgPauseAfterHeader);
        
        cfcPlKSOTurbo.addEntry(cfgPulseCorrections);

        /*Register configuration class*/
        Configuration.registerClass(cfcPlKSOTurbo);
    }

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

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

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

    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, 1 - cfgInvertPolarity.getAsInt(), 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());

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

        int[] dta = dataChunk.getData();
        is.add(dta);

        /*Bezpecnostni pulz*/
        is.add(SignalGenerator.INSTR_NARROW);

    }

    public BinaryConvertor[] getBinaryConvertors() {
        BinaryConvertor[] bcs = new BinaryConvertor[1];
        bcs[0] = new PlKSOTurbo2000BinaryConvertor(this, "KSO Turbo 2000");

        return bcs;
    }

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

    public boolean isUsingAlternateFileChooser() {
        return false;
    }
    
    private void addSilenceAfterHeader(InstructionStream is) {
        int silenceAfterHeader = cfgSilenceAfterHeader.getAsInt();
        if (silenceAfterHeader!=0) {
            is.add(SignalGenerator.INSTR_LONGSILENCE);
            if (cfgPauseAfterHeader.getAsBoolean()==true) silenceAfterHeader=-silenceAfterHeader;
            is.add(silenceAfterHeader);
        }
        else {
            if (cfgPauseAfterHeader.getAsBoolean()==true) {
                is.add(SignalGenerator.INSTR_PAUSE);
            }
        }
    }

}
