package turgen.generator;

import java.io.*;

public class TapeImageTRGenerator {

    public int[] getData(int[] instr) {

        /*Torzo hlavicky data chunku*/
        int[] dataChunkHeader = {100, 97, 116, 97, 0, 0, 0, 0};
        int[] baudChunkHeader = {'b', 'a', 'u', 'd', 0, 0, 0, 0};
        int[] fskChunkHeader = {'f', 's', 'k', ' ', 0, 0, 0, 0};
        int[] fujiChunkHeader = {'F', 'U', 'J', 'I', 0, 0, 0, 0};

        /*Programovy citac*/
        int pc = 0;
        /*Operacni znak*/
        int op = 0;

        /*Novy vektor cisel*/
        int[] retVal = null;

        /*Vystup zatim do proudu bytu*/
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        ByteArrayInputStream bais = null;
        DataInputStream dis = null;

        try {

            if (instr[pc] != SignalGenerator.INSTR_FUJI) {
                /*Nejprve hlavicka = FUJI*/
                dos.writeInt(70);
                dos.writeInt(85);
                dos.writeInt(74);
                dos.writeInt(73);

                /*Dale 4 nuly*/
                for (int i = 0; i < 4; i++) {
                    dos.writeInt(0);
                }
            }

            /*Konec smycky*/
            boolean quit = false;
            /*Je-li k dispozici data chunk, do ktereho se da zapisovat*/
            boolean chunkAvailable = false;

            do {
                /*Operacni znak*/
                if (pc >= instr.length) {
                    break;
                }

                op = instr[pc];
                switch (op) {
                    /*Konec*/
                    case SignalGenerator.INSTR_END: {
                        quit = true;
                        break;
                    }

                    /*Kratky pulz*/
                    case SignalGenerator.INSTR_NARROW: {
                        /*Ignorovat*/
                        pc++;
                        break;
                    }
                    /*Dlouhy pulz*/
                    case SignalGenerator.INSTR_WIDE: {
                        /*Ignorovat*/
                        pc++;
                        break;
                    }
                    /*Synchropulz*/
                    case SignalGenerator.INSTR_SYNC: {
                        /*Ignorovat*/
                        pc++;
                        break;
                    }
                    /*Stop pulse*/
                    case SignalGenerator.INSTR_STOP: {
                        /*Ignore*/
                        pc++;
                        break;
                    }

                    /*0,1 s ticha*/
                    case SignalGenerator.INSTR_SHORTSILENCE: {
                        /*Ignorovat*/
                        pc++;
                        break;
                    }
                    /*Ticho v sekundach*/
                    case SignalGenerator.INSTR_LONGSILENCE: {
                        /*Ignorovat*/
                        pc++;
                        pc++;
                        break;
                    }
                    /*Kolekce zavadecich pulzu*/
                    case SignalGenerator.INSTR_PILOTTONE: {
                        pc++;
                        int cnt = instr[pc];
                        pc++;
                        /*Po zav. tonu je k dispozici data chunk*/
                        chunkAvailable = true;
                        /*Pocet zavadecich pulzu*/
                        dataChunkHeader[6] = cnt % 256;
                        dataChunkHeader[7] = cnt / 256;
                        break;
                    }

                    /*Nastaveni*/
                    case SignalGenerator.INSTR_SETUP: {

                        /*Typ turba a dve nuly*/
                        for (int i = 0; i < 4; i++) {
                            dos.writeInt(instr[pc + 9 + i]);
                        }

                        dos.writeInt(0);
                        dos.writeInt(0);

                        /*Pak rychlost, 2 oktety, little endian*/
                        dos.writeInt(instr[pc + 8] % 256);
                        dos.writeInt(instr[pc + 8] / 256);

                        pc += 13;
                        break;

                    }

                    /*Data, to nejlepsi nakonec*/
                    case SignalGenerator.INSTR_DATA: {
                        pc++;
                        int dataCount = instr[pc];
                        pc += dataCount;
                        pc++;
                        /*Je-li k dispozici data chunk, tak zapsat*/
                        if (chunkAvailable == true) {
                            chunkAvailable = false;
                            dataChunkHeader[4] = dataCount % 256;
                            dataChunkHeader[5] = dataCount / 256;
                            for (int i = 0; i < 8; i++) {
                                dos.writeInt(dataChunkHeader[i]);
                            }
                        }
                        /*Zapsat jednotlive byty data chunku*/
                        int lo = pc - dataCount;
                        int bd = pc;
                        //int k=0;
                        for (int i = lo; i < bd; i++) {
                            dos.writeInt(instr[i]);
                            //k++;
                        }
                        //System.out.println(k);

                        break;
                    }

                    case SignalGenerator.INSTR_PWMS: {
                        pc += 4; /*Always 4 long*/

                        break;
                    }
                    case SignalGenerator.INSTR_PWMC: {
                        pc += 2; /*Skip opcode and silence*/

                        int numPairs = instr[pc];
                        pc++;
                        /*Skip pairs count/len*/
                        for (int i = 0; i < numPairs; i++) {
                            pc += 2;
                        }
                        break;
                    }
                    case SignalGenerator.INSTR_PWMD: {
                        pc++;
                        int numBytes = instr[pc];
                        pc++;
                        pc += 2; /*Skip pulse spec*/

                        for (int i = 0; i < numBytes; i++) {
                            pc++;
                        }
                        break;
                    }
                    case SignalGenerator.INSTR_PWML: {
                        pc += 2; /*Skip opcode and silence*/

                        int numElements = instr[pc];
                        pc++;
                        for (int i = 0; i < numElements; i++) {
                            pc++;
                        }
                        break;
                    }
                    case SignalGenerator.INSTR_BAUD: {
                        pc++;
                        int baudRate = instr[pc];
                        baudChunkHeader[6] = baudRate % 256;
                        baudChunkHeader[7] = baudRate / 256;
                        for (int i = 0; i < baudChunkHeader.length; i++) {
                            dos.writeInt(baudChunkHeader[i]);
                        }
                        pc++;
                        break;
                    }
                    case SignalGenerator.INSTR_STDDATA: {
                        pc++;
                        int irgMilis = instr[pc];
                        pc++;
                        int len = instr[pc];
                        pc++;

                        dataChunkHeader[4] = len % 256;
                        dataChunkHeader[5] = len / 256;
                        dataChunkHeader[6] = irgMilis % 256;
                        dataChunkHeader[7] = irgMilis / 256;

                        for (int i = 0; i < dataChunkHeader.length; i++) {
                            dos.writeInt(dataChunkHeader[i]);
                        }

                        for (int i = 0; i < len; i++) {
                            dos.writeInt(instr[pc]);
                            pc++;
                        }
                        break;
                    }
                    case SignalGenerator.INSTR_FSK: {
                        pc++;
                        int irgMilis = instr[pc];
                        pc++;
                        int lenInWords = instr[pc];
                        int lenInBytes = lenInWords * 2;
                        pc++;

                        fskChunkHeader[4] = lenInBytes % 256;
                        fskChunkHeader[5] = lenInBytes / 256;
                        fskChunkHeader[6] = irgMilis % 256;
                        fskChunkHeader[7] = irgMilis / 256;

                        for (int i = 0; i < fskChunkHeader.length; i++) {
                            dos.writeInt(fskChunkHeader[i]);
                        }

                        for (int i = 0; i < lenInWords; i++) {
                            dos.writeInt(instr[pc] % 256);
                            dos.writeInt(instr[pc] / 256);
                            pc++;
                        }

                        break;

                    }
                    case SignalGenerator.INSTR_FUJI: {
                        /*Skip opcode*/
                        pc++;
                        /*Get length*/
                        int len = instr[pc];
                        pc++;
                        /*Header skeleton*/
                        fujiChunkHeader[4] = len % 256;
                        fujiChunkHeader[5] = len / 256;
                        /*Write header*/
                        for (int i = 0; i < fujiChunkHeader.length; i++) {
                            dos.writeInt(fujiChunkHeader[i]);
                        }
                        /*Write data*/
                        for (int i = 0; i < len; i++) {
                            dos.writeInt(instr[pc]);
                            pc++;
                        }
                        break;
                    }
                    case SignalGenerator.INSTR_PAUSE: {
                        /*Just skip opcode*/
                        pc++;
                        break;
                    }

                    default: {
                        pc++;
                        break;
                    }
                }

            } while (quit == false);

            /*Zpetne nacist*/
            byte[] b = baos.toByteArray();
            int l = b.length / 4;
            bais = new ByteArrayInputStream(baos.toByteArray());
            dis = new DataInputStream(bais);

            retVal = new int[l];
            for (int i = 0; i < l; i++) {
                retVal[i] = dis.readInt();
            }

            dos.close();
            dis.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return retVal;
    }

}
