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.BooleanConfigEntry;
import turgen.config.ConfigClass;
import turgen.config.IntegerConfigEntry;
import turgen.dtb.DOS2Binary;
import turgen.dtb.Segment;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.DataChunk;
import turgen.tapeimage.TapeImageChunk;
import turgen.tapeimage.TurboChunk;


public class PlHardTurboPlugin implements Plugin {

    
    public static final int BL_NONE = 0;
    public static final int BL_HTBLP = 1;
    
    private String lastError;
    private PlHardTurboPluginPanel panel;
    private int[] casChunkName={116,114,112,56};

    private ConfigClass cfcPlHardTurbo;
    private BooleanConfigEntry cfgInvertPolarity;
    private IntegerConfigEntry cfgPilotToneHeader;
    private IntegerConfigEntry cfgPilotToneBlocks;
    private BooleanConfigEntry cfgLoaderNamedAsFile;
    

    /** New instance*/
    public PlHardTurboPlugin() {
    }


    /** Get human readable name
     */
    public String getName() {
        return "Hard Turbo";
    }


    /**Generate instructions for generator
     */
    public int[] getInstructions(AbstractPlayListItem _pl) {


        PlHardTurboPlaylistItem pl;
        pl=(PlHardTurboPlaylistItem)_pl;

        try {

            /* Initialization of streams and initial setup instructions*/
            InstructionStream is = new InstructionStream();
            Utils.addSetupInstruction(is, pl.pulses, casChunkName,/*1-*/cfgInvertPolarity.getAsInt(),SignalGenerator.FLAG_ORDER_HL);

            /*Binary file*/
            convertBinaryHT(is, pl);
            
            /*Finalization*/
            is.add(SignalGenerator.INSTR_END);
            return is.getInstructions();

        }

        catch(Exception e) {
          
            /*Create error message*/
            String msg = e.getMessage();
            lastError="Unable to produce instructions:"+e.toString();
            if (msg!=null) {
                lastError+=" ";
                lastError+=msg;
            }
            e.printStackTrace();
            return null;
        }


    }

    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);
        
        is.add(data);
        

        /*1 safety narrow pulse*/
        is.add(SignalGenerator.INSTR_NARROW);
        
        /*Block separator*/
        is.add(SignalGenerator.INSTR_BLOCKSEP);
        
    }

    

    public String getLastError() {
        return lastError;
    }

    public AbstractPlayListItem createItem() {
        PlHardTurboPlaylistItem 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 PlHardTurboPluginPanel();

        /*Initialize configuration class*/
        cfcPlHardTurbo = new ConfigClass("PlHT", "Hard Turbo");
        cfgInvertPolarity =new BooleanConfigEntry("invertPolarity","Invert polarity of pulses",false);
        
        cfgPilotToneHeader = new IntegerConfigEntry("pilotToneHeader","Header block pilot tone length [pulse]",4096,256,16384);
        cfgPilotToneBlocks = new IntegerConfigEntry("pilotToneBlock","Data block pilot tone length [pulse]",2048,256,16384);
        cfgLoaderNamedAsFile = new BooleanConfigEntry("loaderNamedAsFile","Name loader same as file",false);

        /*Add entries to the class*/
        cfcPlHardTurbo.addEntry(cfgInvertPolarity);
        cfcPlHardTurbo.addEntry(cfgPilotToneHeader);
        cfcPlHardTurbo.addEntry(cfgPilotToneBlocks);
        cfcPlHardTurbo.addEntry(cfgLoaderNamedAsFile);

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

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


    public void setInOutProvider(InOutProvider prov) {

        panel.ioProvider=prov;
    }

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

     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;

        /*Put instructions*/
        addDataBlock(is, dataChunk.getData(), dataChunk.getAux());
        
    }

    public BinaryConvertor[] getBinaryConvertors() {

        BinaryConvertor[] convertors = new BinaryConvertor[2];
        convertors[0]= new PlHardTurboBinaryConvertor(this, "Hard Turbo",BL_NONE);
        convertors[1]= new PlHardTurboBinaryConvertor(this, "Hard Turbo + HTBL+",BL_HTBLP);
        return convertors;

    }


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

    

    private void convertBinaryHT(InstructionStream is, PlHardTurboPlaylistItem pl) throws Exception {

        /*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=cfgPilotToneBlocks.getAsInt();
        int headerPilotPulses=cfgPilotToneHeader.getAsInt();
        
        if (pl.binaryLoader==BL_HTBLP) {
            addHTBLP(headerPilotPulses, blockPilotPulses, pl.fname, is);
        }

        /*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 main header*/
        generateMainHeader(pl.fname, headerPilotPulses, is);

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

            Segment seg = segments[i];
            generateSegment(seg,blockPilotPulses, 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(blockPilotPulses, is);
        
        

    }

    private void calcCheckSum(int[] data) {

        int chsum=data[0];
        for(int i=1;i<data.length-1;i++) {
            chsum=chsum^data[i];
        }
        data[data.length-1]=chsum;
    }
    
    private void generateMainHeader(String displayedName,int pilotTonePulses,InstructionStream is) throws Exception {
        /*Generate header block 41 bytes*/
        int[] fHeader = new int[41];
        fHeader[0]=0;
        
        /*Blanks*/
        for(int i=1;i<fHeader.length-2;i++) {
            fHeader[i]=32;
        }
        /*Place EOL*/
        fHeader[fHeader.length-2]=155;
        String fName=displayedName;
        if (fName.length()>38) fName = fName.substring(0, 38);
        char[] nameChars = fName.toCharArray();
        for(int i=0;i<nameChars.length;i++) {
            fHeader[i+1]=(nameChars[i]);
        }
       
        calcCheckSum(fHeader);
        addDataBlock(is, fHeader, pilotTonePulses);
        
    }
    
    private void generateSegment(Segment seg,int pilotTonePulses,InstructionStream is) throws Exception {
        int[] bHeader = new int[6];
        int sLo=seg.start % 256;
            int sHi=seg.start / 256;
            int eLo=(seg.stop+1) % 256;
            int eHi=(seg.stop+1) / 256;

            bHeader[0]=255;
            bHeader[1]=sLo;
            bHeader[2]=sHi;
            bHeader[3]=eLo;
            bHeader[4]=eHi;
            calcCheckSum(bHeader);
            
            /*Address*/
            addDataBlock(is,bHeader,pilotTonePulses);
            
            /*Segment data*/
            int[] segData = new int[seg.data.length+2];
            segData[0]=255;
            System.arraycopy(seg.data, 0, segData, 1, seg.data.length);
            calcCheckSum(segData);
            addDataBlock(is, segData, pilotTonePulses);
    }
    
    private void generateTermHeader(int pilotTonePulses,InstructionStream is) throws Exception {
        int[] termHeader = new int[6];
        /*Termination header*/
        for(int i=0;i<5;i++) {
            termHeader[i]=255;
        }
        calcCheckSum(termHeader);
        addDataBlock(is,termHeader,pilotTonePulses);
    }
    
    private void addHTBLP(int headerPilotTone,int blockPilotTone,String fname,InstructionStream is) throws Exception {
        
        String headerName;
        if (cfgLoaderNamedAsFile.getAsBoolean()==true) {
            headerName=fname;
        }
        else {
            headerName="HTBL+";
        }
        
        generateMainHeader(headerName, headerPilotTone, is);
        
        Segment seg1 = new Segment(0x2000,0x220D, Segment.DATA_SEGMENT);
        seg1.data=htblpData;
        Segment seg2 = new Segment(736,737,Segment.RUN_SEGMENT,0x21D9);
        
        generateSegment(seg1, blockPilotTone, is);
        generateSegment(seg2, blockPilotTone, is);
        
        generateTermHeader(blockPilotTone, is);
        
    }
    
    int htblpData[] = {

    0x4c, 0x68, 0x09, 0xa9, 0x00, 0x8d, 0x64, 0x08, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0x65, 0x8d, 0xe2, 0x02,
    0x8d, 0xe0, 0x02, 0xa9, 0x08, 0x8d, 0xe1, 0x02, 0x8d, 0xe3, 0x02, 0xa9, 0x27, 0x85, 0x34, 0xa9, 0x04, 0x85, 0x33, 0x85,
    0x35, 0xa9, 0x00, 0x85, 0x32, 0x20, 0xa1, 0x08, 0x90, 0xed, 0xa2, 0x00, 0xa0, 0x04, 0x20, 0x42, 0xc6, 0xa9, 0x3c, 0x8d,
    0x02, 0xd3, 0xa9, 0x01, 0x8d, 0xfc, 0x02, 0x85, 0x09, 0x20, 0xfc, 0xfd, 0xa9, 0x00, 0x8d, 0x63, 0x08, 0x85, 0x32, 0xa9,
    0x04, 0x85, 0x34, 0x85, 0x33, 0x85, 0x35, 0xa9, 0xff, 0x20, 0xa1, 0x08, 0x90, 0x39, 0x20, 0x6f, 0x08, 0xad, 0x00, 0x04,
    0x85, 0x32, 0xad, 0x01, 0x04, 0x85, 0x33, 0xad, 0x02, 0x04, 0x85, 0x34, 0xad, 0x03, 0x04, 0x85, 0x35, 0xa9, 0x65, 0x8d,
    0xe2, 0x02, 0xa9, 0x08, 0x8d, 0xe3, 0x02, 0xa9, 0xff, 0x20, 0xa1, 0x08, 0x90, 0x11, 0xa9, 0x3c, 0x8d, 0x02, 0xd3, 0x20,
    0x60, 0x08, 0x4c, 0x17, 0x08, 0x6c, 0xe2, 0x02, 0x6f, 0x00, 0x60, 0x20, 0x8e, 0xef, 0x20, 0x3e, 0xc6, 0x4c, 0xd2, 0x07,
    0xad, 0x00, 0x04, 0xc9, 0xff, 0xd0, 0x07, 0xad, 0x01, 0x04, 0xc9, 0xff, 0xf0, 0x17, 0xad, 0x64, 0x08, 0xd0, 0xe3, 0xad,
    0x00, 0x04, 0x8d, 0xe0, 0x02, 0xad, 0x01, 0x04, 0x8d, 0xe1, 0x02, 0xee, 0x64, 0x08, 0x4c, 0x65, 0x08, 0xa9, 0x3c, 0x8d,
    0x02, 0xd3, 0xa9, 0x3c, 0x8d, 0x02, 0xd3, 0x6c, 0xe0, 0x02, 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, 0x43, 0x09, 0x90, 0xf9, 0xa9, 0x00, 0x85, 0x2e, 0x85, 0x37, 0xa0, 0xb4, 0x20, 0x3e, 0x09, 0x90,
    0xec, 0xc0, 0xd8, 0x90, 0xea, 0xe6, 0x2e, 0xd0, 0xf1, 0xc6, 0x37, 0xa0, 0xdb, 0x20, 0x43, 0x09, 0x90, 0xdb, 0xc0, 0xe6,
    0xb0, 0xf5, 0x20, 0x43, 0x09, 0x90, 0x44, 0xa0, 0xc6, 0x4c, 0x0d, 0x09, 0x28, 0xd0, 0x08, 0xa5, 0x36, 0x45, 0x2f, 0xd0,
    0x37, 0xf0, 0x0c, 0xa0, 0x00, 0xa5, 0x2f, 0x91, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xa0, 0xc6, 0x08, 0xa9, 0x01,
    0x85, 0x2f, 0x20, 0x3e, 0x09, 0x90, 0x1c, 0xc0, 0xde, 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, 0x60, 0x20, 0x43, 0x09, 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, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0xa9, 0xff,
    0x8d, 0x01, 0xd3, 0x8d, 0xf8, 0x03, 0xa9, 0x84, 0x85, 0x0c, 0xa9, 0x09, 0x85, 0x0d, 0x20, 0x74, 0xe4, 0xa9, 0x8e, 0x85,
    0x0a, 0xa9, 0x09, 0x85, 0x0b, 0x18, 0x60, 0xa2, 0xa0, 0xa0, 0x09, 0x20, 0x42, 0xc6, 0xa9, 0x01, 0x8d, 0xfc, 0x02, 0x20,
    0xfc, 0xfd, 0x4c, 0xd2, 0x07, 0x48, 0x54, 0x42, 0x4c, 0x2b, 0x9b, 0xd7, 0x01, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85,
    0x33, 0xa9, 0xcf, 0x85, 0x34, 0xa9, 0x07, 0x85, 0x35, 0xa0, 0x00, 0xae, 0xd8, 0x21, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34,
    0xc8, 0xd0, 0xf9, 0xe6, 0x33, 0xe6, 0x35, 0xca, 0xd0, 0xf2, 0xae, 0xd7, 0x21, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8,
    0xca, 0xd0, 0xf8, 0x4c, 0xcf, 0x07


    };

    public boolean isUsingAlternateFileChooser() {
        return false;
    }


}
