package turgen.plugin;

import turgen.generator.SignalGenerator;

import java.util.*;
import javax.swing.*;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.DataChunk;
import turgen.tapeimage.TapeImage;
import turgen.tapeimage.TapeImageChunk;
import turgen.*;
import turgen.tapeimage.BaudChunk;
import turgen.tapeimage.PWMChunk;
import turgen.tapeimage.FSKChunk;
import turgen.tapeimage.FujiChunk;

public class TapeImagePlugin implements Plugin {
    private TapeImagePluginPanel panel;
    private InOutProvider ioProvider;

    /** Maps trXX chunks to plugins*/
    private static HashMap chunkSupportMap;

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

    /** Identifikacni jmeno zasuvneho modulu*/
    public String getName() {
        return "Tape image";
    }


    /*************************************************************/

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

        SimplePlaylistItem pl;
        pl = (SimplePlaylistItem) _pl;

        InstructionStream is = new InstructionStream();

        TapeImage ti = new TapeImage();
        ti.parse(pl.inFile);

        TapeImageChunk chunk;
        TapeImageChunk parent;
        int l = ti.getChunkCount();


        /*Browse tape image chunk per chunk. We can be absolutely sure
         that tape image is correctly arranged, because if it is not,
         Execption is already thrown
         */
        for (int i = 0; i < l; i++) {

            chunk = ti.getChunkAt(i);
            parent = chunk.getParent();

            Plugin p = null;

            /*If this is independent chunk, we call plugin directly*/
            if (chunk.isGeneretedUsingParent() == false) {
                p = (Plugin) chunkSupportMap.get(chunk.getType());
            }

            /*If the chunk is dependent we use plugin registred for the
             * parent chunk
             */
            else {
                if (parent != null) {
                    p = (Plugin) chunkSupportMap.get(parent.getType());
                }
            }

            if (p != null) {
                p.addInstructionsForChunk(is, chunk);
            }

            /*Other chunks are currently not supported*/
        }

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



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

    public void resetPanel() {
        
    }

    public String validateItemOnPanel() {
        return null;
    }

    public JPanel getItemPanel() {
        return panel;
    }

    public void init() {
        panel = new TapeImagePluginPanel();
    }

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


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

    public String[] getSupportedTapeImageChunks() {
        String[] chunks = {"FUJI","pwms","pwmc","pwmd","pwml","baud","fsk "};
        return chunks;
    }

    public BinaryConvertor[] getBinaryConvertors() {
        return null;
    }

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

    public static boolean isChunkSupported(TapeImageChunk chunk) {

        if (chunkSupportMap.containsKey(chunk.getType())) return true;
        return false;

    }

    public void mapPlugins(Vector plugins) {

        chunkSupportMap = new HashMap();

        Plugin p;
        Object o;
        
        Iterator it = plugins.iterator();

        /*Get information from all plugins*/
        while(it.hasNext()) {
            p = (Plugin)it.next();
            String[] chunks = p.getSupportedTapeImageChunks();
            
            for(int i=0;i<chunks.length;i++) {
                chunkSupportMap.put(chunks[i], p);
            }
        }
        /*Update panel*/
        Set chunks = chunkSupportMap.keySet();
        int l = chunks.size();
        String[] chunkStrings = new String[l];

        it = chunks.iterator();
        for(int i=0;i<l;i++) {
            chunkStrings[i]=(String)it.next();
        }

        Arrays.sort(chunkStrings, new Comparator() {

            public int compare(Object o1, Object o2) {
               String s1 = (String)o1;
               String s2 = (String)o2;
               return s1.compareTo(s2);
            }

        });

        panel.fillChunkList(chunkStrings);


    }

    public void addInstructionsForChunk(InstructionStream is, TapeImageChunk chunk) throws Exception {
        
        
        if (chunk.getType().equals("pwmc")) {
            processPWMC(is,(PWMChunk)chunk);
            return;
        }
        
        if (chunk.getType().equals("pwmd")) {
            processPWMD(is,(PWMChunk)chunk);
            return;
        }
        
        if (chunk.getType().equals("pwml")) {
            processPWML(is,(PWMChunk)chunk);
        }
        
        if (chunk.getType().equals("pwms")) {
            processPWMS(is,(PWMChunk)chunk);
            return;
        }
        
        if (chunk.getType().equals("baud")) {
            processBAUD(is,(BaudChunk)chunk);
        }
        if (chunk.getType().equals("data")) {
            processDATA(is,(DataChunk)chunk);
        }
        
        if (chunk.getType().equals("fsk ")) {
            processFSK(is,(FSKChunk)chunk);
        }
        if (chunk.getType().equals("FUJI")) {
            processFUJI(is,(FujiChunk)chunk);
        }
        
        
    }
    
    private void processPWMS(InstructionStream is,PWMChunk chunk) throws Exception {
        
        /*Opcode*/
        is.add(SignalGenerator.INSTR_PWMS);
        /*Polarity*/
        if ((chunk.getAuxLo() & 0x00000002)==2) {
            is.add(SignalGenerator.FLAG_POLARITY_10);
        }
        else {
            is.add(SignalGenerator.FLAG_POLARITY_01);
        }
        /*Bit order*/
        if ((chunk.getAuxLo() & 0x00000004)==4) {
            is.add(SignalGenerator.FLAG_ORDER_HL);
        }
        else {
            is.add(SignalGenerator.FLAG_ORDER_LH);
        }
        /*Sample rate*/
        int[] data = chunk.getData();
        is.add(data[0]+data[1]*256);
    }
    
    private void processPWMC(InstructionStream is,PWMChunk chunk) throws Exception {
        
        /*Opcode*/
        is.add(SignalGenerator.INSTR_PWMC);
        /*Silence ms*/
        is.add(chunk.getAux());
        /*Number of pairs*/
        int numPairs = chunk.getLength()/3;
        is.add(numPairs);
        /*Pairs*/
        int pos=0;
        int[] data = chunk.getData();
        for(int i=0;i<numPairs;i++) {
            is.add(data[pos]);
            pos++;
            is.add(data[pos]+256*data[pos+1]);
            pos+=2;
        }
        
    }
    
    private void processPWMD(InstructionStream is,PWMChunk chunk) throws Exception {
        
        /*Opcode*/
        is.add(SignalGenerator.INSTR_PWMD);
        /*Number of bytes*/
        is.add(chunk.getLength());
        /*Narrow pulse*/
        is.add(chunk.getAuxLo());
        /*Wide pulse*/
        is.add(chunk.getAuxHi());
        
        /*Data itself*/
        int[] data = chunk.getData();
        is.add(data);
        
        
    }
    
    private void processPWML(InstructionStream is,PWMChunk chunk) throws Exception {
        
        /*Opcode*/
        is.add(SignalGenerator.INSTR_PWML);
        /*Silence*/
        is.add(chunk.getAux());
        
        /*Number of lengths*/
        is.add(chunk.getLength()/2);
        
        /*Data*/
        int[] data = chunk.getData();
        for(int i=0;i<data.length;i+=2) {
            is.add(data[i]+256*data[i+1]);
        }
        
    }
    
    private void processDATA(InstructionStream is,DataChunk chunk) throws Exception{
        is.add(SignalGenerator.INSTR_STDDATA);
        is.add(chunk.getAux());
        is.add(chunk.getLength());
        /*Data*/
        int[] data = chunk.getData();
        is.add(data);
        
        
    }
    
    private void processBAUD(InstructionStream is,BaudChunk chunk) throws Exception {
        is.add(SignalGenerator.INSTR_BAUD);
        is.add(chunk.getBaudRate());
    }
    
    private void processFSK(InstructionStream is,FSKChunk chunk) throws Exception {
        is.add(SignalGenerator.INSTR_FSK);
        is.add(chunk.getAux());
        is.add(chunk.getLength()/2);
        int[] data = chunk.getData();
        for(int i=0;i<data.length;i+=2) {
            int duration = data[i]+(data[i+1]*256);
            is.add(duration);
        }
    }
    
    private void processFUJI(InstructionStream is, FujiChunk chunk) throws Exception {
        is.add(SignalGenerator.INSTR_FUJI);
        is.add(chunk.getLength());
        int[] data = chunk.getData();
        is.add(data);
        
        
    }

    public boolean isUsingAlternateFileChooser() {
        return true;
    }

}
