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

namespace mame
{
    public class YM3812
    {
        public static Timer.emu_timer[] timer;
        public delegate void ym3812_delegate(LineState linestate);
        public static ym3812_delegate ym3812handler;
        public static void IRQHandler_3812(int irq)
        {
            if (ym3812handler != null)
            {
                ym3812handler(irq != 0 ? LineState.ASSERT_LINE : LineState.CLEAR_LINE);
            }
        }
        public static void timer_callback_3812_0()
        {
            FMOpl.ym3812_timer_over(0);
        }
        public static void timer_callback_3812_1()
        {
            FMOpl.ym3812_timer_over(1);
        }
        private static void TimerHandler_3812(int c, Atime period)
        {
            if (Attotime.attotime_compare(period, Attotime.ATTOTIME_ZERO) == 0)
            {
                Timer.timer_enable(timer[c], false);
            }
            else
            {
                Timer.timer_adjust_periodic(timer[c], period, Attotime.ATTOTIME_NEVER);
            }
        }
        public static void _stream_update_3812(int interval)
        {
            Sound.ym3812stream.stream_update();
        }
        public static void ym3812_start(int clock)
        {
            FMOpl.tl_tab = new int[0x1800];
            FMOpl.sin_tab = new uint[0x1000];
            timer = new Timer.emu_timer[2];
            int rate = clock / 72;
            switch (Machine.sName)
            {
                case "pcktgal":
                case "pcktgalb":
                case "pcktgal2":
                case "pcktgal2j":
                case "spool3":
                case "spool3i":
                    ym3812handler = null;
                    break;
                case "starfigh":
                    ym3812handler = null;
                    break;
                case "drgnwrld":
                case "drgnwrldv30":
                case "drgnwrldv21":
                case "drgnwrldv21j":
                case "drgnwrldv20j":
                case "drgnwrldv10c":
                case "drgnwrldv11h":
                case "drgnwrldv40k":
                    ym3812handler = null;
                    break;
                default:
                    ym3812handler = null;
                    break;
            }
            FMOpl.ym3812_init(0, clock, rate);
            FMOpl.ym3812_set_timer_handler(TimerHandler_3812);
            FMOpl.ym3812_set_irq_handler(IRQHandler_3812);
            FMOpl.ym3812_set_update_handler(_stream_update_3812);
            timer[0] = Timer.timer_alloc_common(timer_callback_3812_0, "timer_callback_3812_0", false);
            timer[1] = Timer.timer_alloc_common(timer_callback_3812_1, "timer_callback_3812_1", false);
        }
        public static void ym3812_control_port_0_w(byte data)
        {
            FMOpl.ym3812_write(0, data);
        }
        public static void ym3812_write_port_0_w(byte data)
        {
            FMOpl.ym3812_write(1, data);
        }
        public static void SaveStateBinary(BinaryWriter writer)
        {
            int i, j;
            for (i = 0; i < 9; i++)
            {
                writer.Write(FMOpl.YM3812.P_CH[i].block_fnum);
                writer.Write(FMOpl.YM3812.P_CH[i].kcode);
                for (j = 0; j < 2; j++)
                {
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].ar);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].dr);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].rr);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].KSR);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].ksl);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].ksr);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].mul);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].Cnt);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].FB);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].op1_out[0]);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].op1_out[1]);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].CON);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].eg_type);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].state);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].TL);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].volume);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].sl);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].key);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].AMmask);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].vib);
                    writer.Write(FMOpl.YM3812.P_CH[i].SLOT[j].wavetable);
                }
            }
            writer.Write(FMOpl.YM3812.eg_cnt);
            writer.Write(FMOpl.YM3812.eg_timer);
            writer.Write(FMOpl.YM3812.rhythm);
            writer.Write(FMOpl.YM3812.lfo_am_depth);
            writer.Write(FMOpl.YM3812.lfo_pm_depth_range);
            writer.Write(FMOpl.YM3812.lfo_am_cnt);
            writer.Write(FMOpl.YM3812.lfo_pm_cnt);
            writer.Write(FMOpl.YM3812.noise_rng);
            writer.Write(FMOpl.YM3812.noise_p);
            writer.Write(FMOpl.YM3812.wavesel);
            for (i = 0; i < 2; i++)
            {
                writer.Write(FMOpl.YM3812.T[i]);
            }
            for (i = 0; i < 2; i++)
            {
                writer.Write(FMOpl.YM3812.st[i]);
            }
            writer.Write(FMOpl.YM3812.address);
            writer.Write(FMOpl.YM3812.status);
            writer.Write(FMOpl.YM3812.statusmask);
            writer.Write(FMOpl.YM3812.mode);
        }
        public static void LoadStateBinary(BinaryReader reader)
        {
            int i, j;
            for (i = 0; i < 9; i++)
            {
                FMOpl.YM3812.P_CH[i].block_fnum = reader.ReadUInt32();
                FMOpl.YM3812.P_CH[i].kcode = reader.ReadByte();
                for (j = 0; j < 2; j++)
                {
                    FMOpl.YM3812.P_CH[i].SLOT[j].ar = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].dr = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].rr = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].KSR = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].ksl = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].ksr = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].mul = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].Cnt = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].FB = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].op1_out[0] = reader.ReadInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].op1_out[1] = reader.ReadInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].CON = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].eg_type = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].state = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].TL = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].volume = reader.ReadInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].sl = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].key = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].AMmask = reader.ReadUInt32();
                    FMOpl.YM3812.P_CH[i].SLOT[j].vib = reader.ReadByte();
                    FMOpl.YM3812.P_CH[i].SLOT[j].wavetable = reader.ReadUInt16();
                }
            }
            FMOpl.YM3812.eg_cnt = reader.ReadUInt32();
            FMOpl.YM3812.eg_timer = reader.ReadUInt32();
            FMOpl.YM3812.rhythm = reader.ReadByte();
            FMOpl.YM3812.lfo_am_depth = reader.ReadByte();
            FMOpl.YM3812.lfo_pm_depth_range = reader.ReadByte();
            FMOpl.YM3812.lfo_am_cnt = reader.ReadUInt32();
            FMOpl.YM3812.lfo_pm_cnt = reader.ReadUInt32();
            FMOpl.YM3812.noise_rng = reader.ReadUInt32();
            FMOpl.YM3812.noise_p = reader.ReadUInt32();
            FMOpl.YM3812.wavesel = reader.ReadByte();
            for (i = 0; i < 2; i++)
            {
                FMOpl.YM3812.T[i] = reader.ReadUInt32();
            }
            for (i = 0; i < 2; i++)
            {
                FMOpl.YM3812.st[i] = reader.ReadByte();
            }
            FMOpl.YM3812.address = reader.ReadByte();
            FMOpl.YM3812.status = reader.ReadByte();
            FMOpl.YM3812.statusmask = reader.ReadByte();
            FMOpl.YM3812.mode = reader.ReadByte();
        }
        public static void IRQHandler_3526(int irq)
        {
            if (ym3812handler != null)
            {
                ym3812handler(irq != 0 ? LineState.ASSERT_LINE : LineState.CLEAR_LINE);
            }
        }
        public static void timer_callback_3526_0()
        {
            FMOpl.ym3526_timer_over(0);
        }
        public static void timer_callback_3526_1()
        {
            FMOpl.ym3812_timer_over(1);
        }
        public static void TimerHandler_3526(int c, Atime period)
        {
            if (Attotime.attotime_compare(period, Attotime.ATTOTIME_ZERO) == 0)
            {
                Timer.timer_enable(timer[c], false);
            }
            else
            {
                Timer.timer_adjust_periodic(timer[c], period, Attotime.ATTOTIME_NEVER);
            }
        }
        public static void _stream_update_3526(int interval)
        {
            Sound.ym3526stream.stream_update();
        }
        public static void ym3526_start(int clock)
        {
            int rate = clock / 72;
            FMOpl.YM3526 = FMOpl.ym3526_init(0, clock, rate);
            timer = new Timer.emu_timer[2];
            FMOpl.ym3526_set_timer_handler(TimerHandler_3526);
            FMOpl.ym3526_set_irq_handler(IRQHandler_3526);
            FMOpl.ym3526_set_update_handler(_stream_update_3526);
            timer[0] = Timer.timer_alloc_common(timer_callback_3526_0, "timer_callback_3526_0", false);
            timer[1] = Timer.timer_alloc_common(timer_callback_3526_1, "timer_callback_3526_1", false);
        }
        public static void ym3526_control_port_0_w(byte data)
        {
            FMOpl.ym3526_write(0, data);
        }
        public static void ym3526_write_port_0_w(byte data)
        {
            FMOpl.ym3526_write(1, data);
        }
        public static byte ym3526_status_port_0_r()
        {
            return FMOpl.ym3526_read(0);
        }
        public static byte ym3526_read_port_0_r()
        {
            return FMOpl.ym3526_read(1);
        }
        public static void ym3526_control_port_1_w(byte data)
        {
            FMOpl.ym3526_write(0, data);
        }
        public static void ym3526_write_port_1_w(byte data)
        {
            FMOpl.ym3526_write(1, data);
        }
        public static byte ym3526_status_port_1_r()
        {
            return FMOpl.ym3526_read(0);
        }
        public static byte ym3526_read_port_1_r()
        {
            return FMOpl.ym3526_read(1);
        }
        public static void SaveStateBinary_YM3526(BinaryWriter writer)
        {
            int i, j;
            for (i = 0; i < 9; i++)
            {
                writer.Write(FMOpl.YM3526.P_CH[i].block_fnum);
                writer.Write(FMOpl.YM3526.P_CH[i].kcode);
                for (j = 0; j < 2; j++)
                {
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].ar);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].dr);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].rr);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].KSR);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].ksl);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].ksr);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].mul);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].Cnt);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].FB);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].op1_out[0]);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].op1_out[1]);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].CON);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].eg_type);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].state);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].TL);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].volume);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].sl);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].key);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].AMmask);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].vib);
                    writer.Write(FMOpl.YM3526.P_CH[i].SLOT[j].wavetable);
                }
            }
            writer.Write(FMOpl.YM3526.eg_cnt);
            writer.Write(FMOpl.YM3526.eg_timer);
            writer.Write(FMOpl.YM3526.rhythm);
            writer.Write(FMOpl.YM3526.lfo_am_depth);
            writer.Write(FMOpl.YM3526.lfo_pm_depth_range);
            writer.Write(FMOpl.YM3526.lfo_am_cnt);
            writer.Write(FMOpl.YM3526.lfo_pm_cnt);
            writer.Write(FMOpl.YM3526.noise_rng);
            writer.Write(FMOpl.YM3526.noise_p);
            writer.Write(FMOpl.YM3526.wavesel);
            for (i = 0; i < 2; i++)
            {
                writer.Write(FMOpl.YM3526.T[i]);
            }
            for (i = 0; i < 2; i++)
            {
                writer.Write(FMOpl.YM3526.st[i]);
            }
            writer.Write(FMOpl.YM3526.address);
            writer.Write(FMOpl.YM3526.status);
            writer.Write(FMOpl.YM3526.statusmask);
            writer.Write(FMOpl.YM3526.mode);
        }
        public static void LoadStateBinary_YM3526(BinaryReader reader)
        {
            int i, j;
            for (i = 0; i < 9; i++)
            {
                FMOpl.YM3526.P_CH[i].block_fnum = reader.ReadUInt32();
                FMOpl.YM3526.P_CH[i].kcode = reader.ReadByte();
                for (j = 0; j < 2; j++)
                {
                    FMOpl.YM3526.P_CH[i].SLOT[j].ar = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].dr = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].rr = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].KSR = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].ksl = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].ksr = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].mul = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].Cnt = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].FB = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].op1_out[0] = reader.ReadInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].op1_out[1] = reader.ReadInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].CON = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].eg_type = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].state = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].TL = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].volume = reader.ReadInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].sl = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].key = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].AMmask = reader.ReadUInt32();
                    FMOpl.YM3526.P_CH[i].SLOT[j].vib = reader.ReadByte();
                    FMOpl.YM3526.P_CH[i].SLOT[j].wavetable = reader.ReadUInt16();
                }
            }
            FMOpl.YM3526.eg_cnt = reader.ReadUInt32();
            FMOpl.YM3526.eg_timer = reader.ReadUInt32();
            FMOpl.YM3526.rhythm = reader.ReadByte();
            FMOpl.YM3526.lfo_am_depth = reader.ReadByte();
            FMOpl.YM3526.lfo_pm_depth_range = reader.ReadByte();
            FMOpl.YM3526.lfo_am_cnt = reader.ReadUInt32();
            FMOpl.YM3526.lfo_pm_cnt = reader.ReadUInt32();
            FMOpl.YM3526.noise_rng = reader.ReadUInt32();
            FMOpl.YM3526.noise_p = reader.ReadUInt32();
            FMOpl.YM3526.wavesel = reader.ReadByte();
            for (i = 0; i < 2; i++)
            {
                FMOpl.YM3526.T[i] = reader.ReadUInt32();
            }
            for (i = 0; i < 2; i++)
            {
                FMOpl.YM3526.st[i] = reader.ReadByte();
            }
            FMOpl.YM3526.address = reader.ReadByte();
            FMOpl.YM3526.status = reader.ReadByte();
            FMOpl.YM3526.statusmask = reader.ReadByte();
            FMOpl.YM3526.mode = reader.ReadByte();
        }
    }
}
