using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;


namespace sega_fusion
{

    public partial class RomFormatException : Exception
    {
        public RomFormatException(String mess)
            : base(mess)
        {

        }
    }


    public class Rom
    {

        public const int PLATFROM_SEGA = 1;



        bool is_loader;
        String rom_name = "no name";
        String rom_region = "default";
        String rom_type_str = "unknown";
        byte[] rom_binary;
        int idx = 0;

        public Rom(String filename, String region)
        {
            is_loader = false;
            try
            {
                loadFromZip(filename, region);
                if (rom_binary == null)
                {
                    loadFromFile(filename, region);
                }
            }
            catch (RomFormatException x)
            {
                throw new Exception("can not load rom: " + x.Message);
            }



        }

        void loadFromFile(String filename, String region)
        {
            byte[] buff = null;
            FileStream fis = new System.IO.FileStream(filename, System.IO.FileMode.Open);
            if (fis.Length > 8388608 * 4)
            {
                throw new RomFormatException("file to big");
            }
            buff = new byte[fis.Length];
            fis.Read(buff, 0, buff.Length);
            fis.Close();
            rom_binary = loadRom(buff);
            rom_name = filename;
            fis.Close();
        }

        void loadFromZip(String filename, String region)
        {
            rom_region = region;
            FileStream f = File.OpenRead(filename);
            byte[] buff = null;



            ZipInputStream zis = new ZipInputStream(f);
            ZipEntry entry = null;

            for (int i = 0; i < 10; i++)
            {
                try
                {
                    entry = zis.GetNextEntry();
                }
                catch (Exception)
                {
                    break;
                }

                try
                {
                    if (!entry.ToString().Contains(".bin") && 
                        !entry.ToString().Contains(".smd") && 
                        !entry.ToString().Contains(".gen") && 
                        !entry.ToString().Contains(".smc") &&
                        !entry.ToString().Contains(".v64") &&
                        !entry.ToString().Contains(".z64") && 
                        !entry.ToString().Contains(".rom"))
                    {
                        continue;
                    }
                    if (zis.Length > 8388608 * 4)
                    {
                        continue;
                    }
                    buff = new byte[zis.Length];
                    zis.Read(buff, 0, buff.Length);

                    rom_binary = loadRom(buff);
                }
                catch (RomFormatException)
                {
                    continue;
                }


                rom_name = entry.ToString();
                break;
            }

            zis.Close();
            f.Close();

        }

        byte[] loadRom(byte[] res)
        {

            byte []buff = loadSegaRom(res);
            if (buff.Length < 262144)
            {
                byte[] buff2 = new byte[262144];
                System.Array.Copy(buff, 0, buff2, 0, buff.Length);
                return buff2;
            }
            int len = buff.Length;
            while (len > 262144 && len != 262144) len /= 2;

            if (len != 262144)
            {
                len = 262144;
                while (len < buff.Length) len <<= 1;
                
                byte[] buff2 = new byte[len];
                System.Array.Copy(buff, 0, buff2, 0, buff.Length);
                return buff2;
            }
            return buff;

        }
        


        byte[] loadSegaRom(byte[] rom_src)
        {

            if (rom_src.Length < 16384)
            {
                throw new RomFormatException("file size to small");
            }
            byte[] rom = null;
            int type_bin = 0;
            int type_smd = 1;

            int rom_type = type_bin;
            long blocks = 0;

            blocks = (rom_src.Length - 512) / 16384;
            int byte_a = rom_src[0] & 0xFF;
            int byte_b = rom_src[1] & 0xFF;
            int e = 0;
            int o = 0;
            byte[] bin_block = new byte[16384];
            byte[] smd_block = new byte[16384];
            int rom_ptr = 0;

            if (byte_a == blocks && byte_b == 3)
            {
                rom_type = type_smd;
            }

            if (rom_type == type_bin)
            {
                rom_type_str = "SEGA bin";
                rom = rom_src;
            }
            else if (rom_type == type_smd)
            {
                rom_type_str = "SEGA smd";

                rom = new byte[blocks * 16384];


                for (int i = 0; i < blocks; i++)
                {
                    e = 0;
                    o = 1;
                    //fis.Read(smd_block, 0, 16384);
                    System.Array.Copy(rom_src, 16384 * i + 512, smd_block, 0, 16384);

                    for (int j = 0; j < 16384; j++)
                    {
                        if (j < 8192)
                        {
                            bin_block[o] = smd_block[j];
                            o += 2;
                        }
                        else
                        {
                            bin_block[e] = smd_block[j];
                            e += 2;
                        }
                    }

                    System.Array.Copy(bin_block, 0, rom, rom_ptr, bin_block.Length);
                    rom_ptr += bin_block.Length;

                }

            }
            else
            {
                throw new RomFormatException("unknown rom format");
            }

            if (!rom_region.Equals("default"))
            {
                char[] str = rom_region.ToCharArray();
                rom[0x1F0] = (byte)str[0];
                rom[0x1F0 + 1] = 0x20;
                rom[0x1F0 + 2] = 0x20;
                rom[0x1F0 + 3] = 0x20;
            }

            return rom;

        }

      
        public byte[] Binary
        {
            get
            {
                return rom_binary;
            }
        }

        public String Name
        {
            get
            {
                return ToString();
            }
        }
        
        

        public override String ToString()
        {
            char[] name = new char[48];
            for (int i = 0; i < 48; i++)
            {
                name[i] = (char)rom_binary[336 + i];
            }
            String str = new String(name).Trim();

            return str;
        }

        public bool IsLoader
        {
            get
            {
                return is_loader;
            }

            set
            {
                is_loader = value;
            }
        }

        public byte[] getByteName(int len)
        {
            byte[] buff = new byte[len];
            for (int i = 0; i < len - 1; i++)
            {
                buff[i] = rom_binary[288 + i];
            }
            return buff;
        }

        public int Idx
        {
            get { return idx; }
            set { idx = value; }
        }

   
    }




}
