using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace mame
{
    /*public enum eeprom_command
    {
        COMMAND_INVALID,
        COMMAND_READ,
        COMMAND_WRITE,
        COMMAND_ERASE,
        COMMAND_LOCK,
        COMMAND_UNLOCK,
        COMMAND_WRITEALL,
        COMMAND_ERASEALL,
        COMMAND_COPY_EEPROM_TO_RAM,
        COMMAND_COPY_RAM_TO_EEPROM
    }
    public enum eeprom_state
    {
        STATE_IN_RESET,
        STATE_WAIT_FOR_START_BIT,
        STATE_WAIT_FOR_COMMAND,
        STATE_READING_DATA,
        STATE_WAIT_FOR_DATA,
        STATE_WAIT_FOR_COMPLETION
    };
    public enum eeprom_event
    {
        EVENT_CS_RISING_EDGE = 1 << 0,
        EVENT_CS_FALLING_EDGE = 1 << 1,
        EVENT_CLK_RISING_EDGE = 1 << 2,
        EVENT_CLK_FALLING_EDGE = 1 << 3
    };*/
    public class Eeprom
    {
        public static int serial_count;
        public static byte[] serial_buffer = new byte[40];
        public static byte[] eeprom_data = new byte[0x80];
        private static byte[] cmd_read;
        private static byte[] cmd_write;
        private static byte[] cmd_erase;
        private static byte[] cmd_lock, cmd_unlock;
        public static int eeprom_data_bits;
        public static int eeprom_read_address;
        public static int eeprom_clock_count;
        public static int latch, sending;
        public static int address_bits, data_bits;
        public static LineState reset_line, clock_line;
        public static int locked;
        public static int enable_multi_read;
        public static int reset_delay,reset_delay1;
        private static bool eeprom_command_match(byte[] buf, byte[] cmd, int len)
        {
            int ibuf = 0, idx = 0;
            if (cmd.Length == 0)
            {
                return false;
            }
            if (len == 0)
            {
                return false;
            }
            for (; len > 0; )
            {
                byte b = buf[ibuf];
                byte c = cmd[idx];
                if ((b == 0) || (c == 0))
                    return (b == c);
                switch (c)
                {
                    case (byte)'0':
                    case (byte)'1':
                        if (b != c)
                        {
                            return false;
                        }
                        ibuf++;
                        len--;
                        idx++;
                        break;
                    case (byte)'X':
                    case (byte)'x':
                        ibuf++;
                        len--;
                        idx++;
                        break;
                    case (byte)'*':
                        break;
                }
            }
            return (idx >= cmd.Length);
        }
        public static void eeprom_init()
        {
            serial_count = 0;
            latch = 0;
            reset_line = LineState.ASSERT_LINE;
            clock_line = LineState.ASSERT_LINE;
            eeprom_read_address = 0;
            sending = 0;
            reset_delay1 = 0;
            switch (Machine.sBoard)
            {
                case "CPS-1"://pang3
                    cmd_read = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'0' };
                    cmd_write = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'1' };
                    cmd_erase = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'1' };
                    cmd_lock = new byte[] { };
                    cmd_unlock = new byte[] { };
                    for (int i = 0; i < 0x80; i++)
                    {
                        eeprom_data[i] = 0xff;
                    }
                    locked = 0;
                    address_bits = 6;
                    data_bits = 16;
                    break;
                case "CPS-1(QSound)":
                    cmd_read = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'0' };
                    cmd_write = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'1' };
                    cmd_erase = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'1' };
                    cmd_lock = new byte[] { };
                    cmd_unlock = new byte[] { };
                    for (int i = 0; i < 0x80; i++)
                    {
                        eeprom_data[i] = 0xff;
                    }                    
                    locked = 0;
                    address_bits = 7;
                    data_bits = 8;
                    break;
                case "CPS2":
                    cmd_read = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'0' };
                    cmd_write = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'1' };
                    cmd_erase = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'1' };
                    cmd_lock = new byte[] { };
                    cmd_unlock = new byte[] { };
                    for (int i = 0; i < 0x80; i++)
                    {
                        eeprom_data[i] = 0xff;
                    }
                    locked = 0;
                    address_bits = 6;
                    data_bits = 16;
                    break;
                case "Taito B":
                    cmd_read = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'0' };
                    cmd_write = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'1' };
                    cmd_erase = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'1' };
                    cmd_lock = new byte[] { (byte)'0',(byte)'1',(byte)'0',(byte)'0',(byte)'0',(byte)'0',(byte)'0',(byte)'0',(byte)'0',(byte)'0' };
                    cmd_unlock = new byte[] {(byte)'0',(byte)'1',(byte)'0',(byte)'0',(byte)'1',(byte)'1',(byte)'0',(byte)'0',(byte)'0',(byte)'0' };
                    for (int i = 0; i < 0x80; i++)
                    {
                        eeprom_data[i] = 0xff;
                    }
                    locked = 1;
                    address_bits = 6;
                    data_bits = 16;
                    break;
                case "Konami 68000":                    
                    cmd_read = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'0', (byte)'0', (byte)'0' };
                    cmd_write = new byte[] { (byte)'0', (byte)'1', (byte)'1', (byte)'1', (byte)'0', (byte)'0' };
                    cmd_erase = new byte[] { };
                    cmd_lock = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0' };
                    cmd_unlock = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'0', (byte)'1', (byte)'1', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0', (byte)'0' };
                    for (int i = 0; i < 0x80; i++)
                    {
                        eeprom_data[i] = 0xff;
                    }
                    locked = 1;
                    address_bits = 7;
                    data_bits = 8;
                    switch (Machine.sName)
                    {
                        case "thndrx2":
                        case "thndrx2a":
                        case "thndrx2j":
                        case "prmrsocr":
                        case "prmrsocrj":
                            cmd_write = new byte[] { (byte)'0', (byte)'1', (byte)'0', (byte)'1', (byte)'0', (byte)'0' };
                            break;
                    }
                    break;
            }
            switch (Machine.sName)
            {
                case "pang3":
                case "pang3r1":
                case "pang3j":
                case "pang3b":
                    for (int i = 0; i < 0x80; i++)
                    {
                        eeprom_data[i] = 0xff;
                    }
                    locked = 0;
                    address_bits = 6;
                    data_bits = 16;
                    break;
            }
        }
        private static void eeprom_write(int bit)
        {
            if (serial_count >= 40 - 1)
            {
                return;
            }
            serial_buffer[serial_count++] = (bit != 0 ? (byte)'1' : (byte)'0');
            serial_buffer[serial_count] = 0;	/* nul terminate so we can treat it as a string */
            if ((serial_count > address_bits) && eeprom_command_match(serial_buffer, cmd_read, serial_count - address_bits))
            {
                int i, address;
                address = 0;
                for (i = serial_count - address_bits; i < serial_count; i++)
                {
                    address <<= 1;
                    if (serial_buffer[i] == (byte)'1')
                    {
                        address |= 1;
                    }
                }
                if (data_bits == 16)
                {
                    eeprom_data_bits = (eeprom_data[2 * address + 0] << 8) + eeprom_data[2 * address + 1];
                }
                else
                {
                    eeprom_data_bits = eeprom_data[address];
                }
                eeprom_read_address = address;
                eeprom_clock_count = 0;
                sending = 1;
                serial_count = 0;
            }
            else if ((serial_count > address_bits) && eeprom_command_match(serial_buffer, cmd_erase, serial_count - address_bits))
            {
                int i, address;
                address = 0;
                for (i = serial_count - address_bits; i < serial_count; i++)
                {
                    address <<= 1;
                    if (serial_buffer[i] == '1')
                    {
                        address |= 1;
                    }
                }
                if (locked == 0)
                {
                    if (data_bits == 16)
                    {
                        eeprom_data[2 * address + 0] = 0x00;
                        eeprom_data[2 * address + 1] = 0x00;
                    }
                    else
                    {
                        eeprom_data[address] = 0x00;
                    }
                }
                serial_count = 0;
            }
            else if ((serial_count > (address_bits + data_bits)) && eeprom_command_match(serial_buffer, cmd_write, serial_count - (address_bits + data_bits)))
            {
                int i, address, data;
                address = 0;
                for (i = serial_count - data_bits - address_bits; i < (serial_count - data_bits); i++)
                {
                    address <<= 1;
                    if (serial_buffer[i] == '1')
                    {
                        address |= 1;
                    }
                }
                data = 0;
                for (i = serial_count - data_bits; i < serial_count; i++)
                {
                    data <<= 1;
                    if (serial_buffer[i] == '1')
                    {
                        data |= 1;
                    }
                }
                if (locked == 0)
                {
                    if (data_bits == 16)
                    {
                        eeprom_data[2 * address + 0] = (byte)(data >> 8);
                        eeprom_data[2 * address + 1] = (byte)(data & 0xff);
                    }
                    else
                    {
                        eeprom_data[address] = (byte)data;
                    }
                }
                serial_count = 0;
            }
            else if (eeprom_command_match(serial_buffer, cmd_lock, serial_count))
            {
                locked = 1;
                serial_count = 0;
            }
            else if (eeprom_command_match(serial_buffer, cmd_unlock, serial_count))
            {
                locked = 0;
                serial_count = 0;
            }
        }
        private static void eeprom_reset()
        {
            serial_count = 0;
            sending = 0;
            reset_delay = reset_delay1;
        }
        public static void eeprom_write_bit(int bit)
        {
            latch = bit;
        }
        public static int eeprom_read_bit()
        {
            int res;
            if (sending == 1)
            {
                res = (eeprom_data_bits >> data_bits) & 1;
            }
            else
            {
                if (reset_delay > 0)
                {
                    reset_delay--;
                    res = 0;
                }
                else
                {
                    res = 1;
                }
            }
            return res;
        }
        public static int eeprom_bit_r()
        {
            return eeprom_read_bit();
        }
        public static void eeprom_set_cs_line(LineState state)
        {
            reset_line = state;
            if (reset_line != LineState.CLEAR_LINE)
            {
                eeprom_reset();
            }
        }
        public static void eeprom_set_clock_line(LineState state)
        {
            if (state == LineState.PULSE_LINE || (clock_line == LineState.CLEAR_LINE && state != LineState.CLEAR_LINE))
            {
                if (reset_line == LineState.CLEAR_LINE)
                {
                    if (sending == 1)
                    {
                        if (eeprom_clock_count == data_bits && enable_multi_read!=0)
                        {
                            eeprom_read_address = (eeprom_read_address + 1) & ((1 << address_bits) - 1);
                            if (data_bits == 16)
                            {
                                eeprom_data_bits = (eeprom_data[2 * eeprom_read_address + 0] << 8) + eeprom_data[2 * eeprom_read_address + 1];
                            }
                            else
                            {
                                eeprom_data_bits = eeprom_data[eeprom_read_address];
                            }
                            eeprom_clock_count = 0;
                        }
                        eeprom_data_bits = (eeprom_data_bits << 1) | 1;
                        eeprom_clock_count++;
                    }
                    else
                    {
                        eeprom_write(latch);
                    }
                }
            }
            clock_line = state;
        }
        public static void SaveStateBinary(BinaryWriter writer)
        {
            writer.Write(eeprom_data);
            writer.Write(serial_buffer);
            writer.Write((int)clock_line);
            writer.Write((int)reset_line);
            writer.Write(locked);
            writer.Write(serial_count);
            writer.Write(latch);
            writer.Write(reset_delay);
            writer.Write(sending);
            writer.Write(eeprom_clock_count);
            writer.Write(eeprom_data_bits);
            writer.Write(eeprom_read_address);
        }
        public static void LoadStateBinary(BinaryReader reader)
        {
            eeprom_data = reader.ReadBytes(0x80);
            serial_buffer = reader.ReadBytes(40);
            clock_line = (LineState)reader.ReadInt32();
            reset_line = (LineState)reader.ReadInt32();
            locked = reader.ReadInt32();
            serial_count = reader.ReadInt32();
            latch = reader.ReadInt32();
            reset_delay = reader.ReadInt32();
            sending = reader.ReadInt32();
            eeprom_clock_count = reader.ReadInt32();
            eeprom_data_bits = reader.ReadInt32();
            eeprom_read_address = reader.ReadInt32();
        }
    }
}
