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 String lastError;
    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[] getInstructions(AbstractPlayListItem _pl) {

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

       try {

            /*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();

        } catch (Exception e) {
            
            e.printStackTrace();
            if (e.getMessage() == null) {
                lastError = "Unable to produce instructions: " + e.toString();
            } else {
                lastError = e.toString();
            }
            return null;

        }
    }

    public String getLastError() {
        return lastError;
    }

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

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

    public String validateInput() {
        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

    };

}
