using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LibEveryFileExplorer.Files;
using System.Drawing;
using System.IO;
using _3DS.UI;
using LibEveryFileExplorer.IO;

namespace _3DS
{
    public class ThemeFile : FileFormat<ThemeFile.ThemeIdentifier>, IViewable, IWriteable
    {

        public byte[] Write()
        {
            MemoryStream m = new MemoryStream();
            EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian);

            er.BaseStream.Position = 0xD0;//just after header - normal start of data for theme files

            header.topScreenTextureOffset = (uint)er.BaseStream.Position;
            er.Write(topScreenTexture, 0, topScreenTexture.Length);

            header.bottomScreenSolidOrTextureOffset = (uint)er.BaseStream.Position;
            er.Write(bottomScreenTexture, 0, bottomScreenTexture.Length);

            header.openedFolderTextureOffset = (uint)er.BaseStream.Position;
            er.Write(openFolderTexture, 0, openFolderTexture.Length);

            header.closedFolderTextureOffset = (uint)er.BaseStream.Position;
            er.Write(closedFolderTexture, 0, closedFolderTexture.Length);

            header.iconBorder48pxOffset = (uint)er.BaseStream.Position;
            er.Write(iconBorder48pxTexture, 0, iconBorder48pxTexture.Length);

            header.iconBorder24pxOffset = (uint)er.BaseStream.Position;
            er.Write(iconBorder24pxTexture, 0, iconBorder24pxTexture.Length);

            header.Write(er);

            return m.ToArray();
        }
        public string GetSaveDefaultFileFilter()
        {
            return "System Menu Theme (body_LZ.bin)|body_LZ.bin";
        }

        public Color topSolidColor;
        public Color bottomSolidColor;
        public ThemeFile(byte[] Data)
        {
            EndianBinaryReader er = new EndianBinaryReader(new MemoryStream(Data), Endianness.LittleEndian);
            try
            {
                header = new ThemeHeader(er);

                topHeight = 256;
                bottomHeight = 256;

                switch (header.topScreenDrawType)
                {
                    case 1://solid color
                        er.BaseStream.Position = header.topScreenSolidColorDataOffset;//7 bytes here, first 4 is rgba, other 3 unknown (possible overlay colour ??)
                        Byte r = er.ReadByte();
                        Byte g = er.ReadByte();
                        Byte b = er.ReadByte();
                        Byte a1 = er.ReadByte();//alpha makes the colours appear lighter because the 3ds does blending
                        topSolidColor = Color.FromArgb(a1, r, g, b);
                        break;

					case 2://wierd 64x64 overlay mode
						topWidth = 64;
						topHeight = 64;
						topClampWidth = 64;
						topClampHeight = 64;
						break;
                    case 3://texture
                        switch (header.topScreenFrameType)
                        {
                            case 0://tex1
                            case 3://tex1
                                topWidth = 1024;
                                topClampWidth = 1008;
                                topClampHeight = 240;
                                break;
                            case 1://tex0
                                topWidth = 512;
                                topClampWidth = 412;
                                topClampHeight = 240;
                                break;
                        }
                        break;
                }

                switch (header.bottomScreenDrawType)
                {
                    case 1://solid color
                        er.BaseStream.Position = header.bottomScreenSolidOrTextureOffset;//7 bytes here, first 4 is rgba, other 3 unknown (possible overlay colour ??)
                        Byte r = er.ReadByte();
                        Byte g = er.ReadByte();
                        Byte b = er.ReadByte();
                        Byte a1 = er.ReadByte();//alpha makes the colours appear lighter because the 3ds does blending

                        bottomSolidColor = Color.FromArgb(a1, r, g, b);
                        break;

                    case 3://texture
                        switch (header.bottomScreenFrameType)
                        {
                            case 0://tex4
                            case 3://tex4
                                bottomWidth = 1024;
                                bottomClampWidth = 1008;
                                bottomClampHeight = 240;
                                break;

                            case 1://tex2
                                bottomWidth = 512;
                                bottomClampWidth = 320;
                                bottomClampHeight = 240;
                                break;

							case 4://tex3 //both animated.?
                            case 2://tex3
                                bottomWidth = 1024;
                                bottomClampWidth = 960;
                                bottomClampHeight = 240;
                                break;

                        }
                        break;
                }



                er.BaseStream.Position = header.topScreenTextureOffset;
				if (header.topScreenDrawType == 2)// in draw type 2 theyre are 2 alpha images, one at topscreentextureoffset and the other at the colour offset, they need to be drawn onto the gradient background
				{
					topOverlayAlphaTexture = er.ReadBytes(alphaTextureWidth * alphaTextureHeight);
					er.BaseStream.Position = header.topScreenAdditionalTextureOffset;
					topBackgroundAlphaTexture = er.ReadBytes(alphaTextureWidth * alphaTextureHeight);

					//TODO figure out colour data for gradients etc
					er.BaseStream.Position = header.topScreenSolidColorDataOffset;
					Byte type = er.ReadByte();
					Byte r1 = er.ReadByte();
					Byte g1 = er.ReadByte();
					Byte b1 = er.ReadByte();

					Byte r2 = er.ReadByte();
					Byte g2 = er.ReadByte();
					Byte b2 = er.ReadByte();

					Console.WriteLine("type? " + type);
					Console.WriteLine("color " + r1 + " " + g1 + " " + b1);
					Console.WriteLine("color " + r2 + " " + g2 + " " + b2);

				}
				else
					topScreenTexture = er.ReadBytes((topHeight * topWidth) * 2);

                er.BaseStream.Position = header.bottomScreenSolidOrTextureOffset;
                bottomScreenTexture = er.ReadBytes((bottomHeight * bottomWidth) * 2);

                er.BaseStream.Position = header.openedFolderTextureOffset;
                openFolderTexture = er.ReadBytes((folderWidth * folderHeight) * 4);
                er.BaseStream.Position = header.closedFolderTextureOffset;
                closedFolderTexture = er.ReadBytes((folderWidth * folderHeight) * 4);

                er.BaseStream.Position = header.iconBorder48pxOffset;
                iconBorder48pxTexture = er.ReadBytes((iconBorder48pxWidth * iconBorder48pxHeight) * 4);
                er.BaseStream.Position = header.iconBorder24pxOffset;
                iconBorder24pxTexture = er.ReadBytes((iconBorder24pxWidth * iconBorder24pxHeight) * 4);

				er.BaseStream.Position = header.audioSectionOffset;
				audioSection = new AudioSection(er);




            }
            finally
            {
                er.Close();
            }

        }

		public Image testImage;

        public int iconBorder48pxWidth = 64;
        public int iconBorder48pxHeight = 128;
        public byte[] iconBorder48pxTexture;
        public Bitmap GetIconBorder48px(bool clamp)
        {
            if (clamp)
                return GPU.Textures.ToBitmap(iconBorder48pxTexture, 36, 72, GPU.Textures.ImageFormat.RGB8, false);
            return GPU.Textures.ToBitmap(iconBorder48pxTexture, iconBorder48pxWidth, iconBorder48pxHeight, GPU.Textures.ImageFormat.RGB8, true);
        }
        public int iconBorder24pxWidth = 32;
        public int iconBorder24pxHeight = 64;
        public byte[] iconBorder24pxTexture;
        public Bitmap GetIconBorder24px(bool clamp)
        {
            if (clamp)
                return GPU.Textures.ToBitmap(iconBorder24pxTexture, 25, 50, GPU.Textures.ImageFormat.RGB8, false);
            return GPU.Textures.ToBitmap(iconBorder24pxTexture, iconBorder24pxWidth, iconBorder24pxHeight, GPU.Textures.ImageFormat.RGB8, true);
        }


        public int folderWidth = 128;
        public int folderHeight = 64;
        public byte[] openFolderTexture;
        public byte[] closedFolderTexture;
        public Bitmap GetOpenFolderTexture(bool clamp)
        {
            if (clamp)
                return GPU.Textures.ToBitmap(openFolderTexture, 82, 64, GPU.Textures.ImageFormat.RGB8, false);
            return GPU.Textures.ToBitmap(openFolderTexture, folderWidth, folderHeight, GPU.Textures.ImageFormat.RGB8, true);
        }
        public Bitmap GetClosedFolderTexture(bool clamp)
        {
            if (clamp)
                return GPU.Textures.ToBitmap(closedFolderTexture, 74, 64, GPU.Textures.ImageFormat.RGB8, false);
            return GPU.Textures.ToBitmap(closedFolderTexture, folderWidth, folderHeight, GPU.Textures.ImageFormat.RGB8, true);
        }


        public int bottomWidth;
        public int bottomHeight;
        public byte[] bottomScreenTexture;
        public int bottomClampWidth;
        public int bottomClampHeight;

        public Bitmap GetBottomTexture(bool clamp)
        {
            if (clamp)
                return GPU.Textures.ToBitmap(bottomScreenTexture, bottomClampWidth, bottomClampHeight, GPU.Textures.ImageFormat.RGB565, false);
            return GPU.Textures.ToBitmap(bottomScreenTexture, bottomWidth, bottomHeight, GPU.Textures.ImageFormat.RGB565, true);
        }

        public int topWidth;
        public int topHeight;
        public byte[] topScreenTexture;
        public int topClampWidth;
        public int topClampHeight;
        public Bitmap GetTopTexture(bool clamp)
        {
            if (clamp)
                return GPU.Textures.ToBitmap(topScreenTexture, topClampWidth, topClampHeight, GPU.Textures.ImageFormat.RGB565, false);
            return GPU.Textures.ToBitmap(topScreenTexture, topWidth, topHeight, GPU.Textures.ImageFormat.RGB565, true);
        }

		public byte[] topOverlayAlphaTexture;
		public byte[] topBackgroundAlphaTexture;
		public int alphaTextureWidth = 64;
		public int alphaTextureHeight = 64;
		public Bitmap GetOverlayAlphaTexture()///Overlayed in a moving pattern ontop of the TopAlphaTexture 
		{
			return GPU.Textures.ToBitmap(topOverlayAlphaTexture, alphaTextureWidth, alphaTextureHeight, GPU.Textures.ImageFormat.A8, true);
		}
		public Bitmap GetBackgroundAlphaTexture()//Static texture background of top screen, does not move.
		{
			return GPU.Textures.ToBitmap(topBackgroundAlphaTexture, alphaTextureWidth, alphaTextureHeight, GPU.Textures.ImageFormat.A8, true);
		}


        public System.Windows.Forms.Form GetDialog()
        {
            return new ThemeViewer(this);
        }

        public AudioSection audioSection;
        public class AudioSection
        {
            public AudioSection(EndianBinaryReader er)
            {
				Console.WriteLine("audio section at " + er.BaseStream.Position);
				version = er.ReadUInt32();
				flag = er.ReadUInt32();

				//start of cwav
				
				cwavSize = er.ReadUInt32();
				er.ReadUInt32();//2nd flag?

				Console.WriteLine("cwavSize " + cwavSize);
				
				//cwav
				Console.WriteLine("cwav at " + er.BaseStream.Position);


            }

			UInt32 version;
			UInt32 flag;
			byte[] cwav6ExtraData;
			UInt32 cwavSize;
			byte[] cwav4ExtraData;
        }

        public ThemeHeader header;
        public class ThemeHeader
        {


            public ThemeHeader(EndianBinaryReader er)
            {
                //offset 0x0
                version = er.ReadUInt32();

                unknownByte1 = er.ReadByte();
                backgroundMusicEnabled = er.ReadByte() == 1;
                unknownByte2 = er.ReadByte();
                unknownByte3 = er.ReadByte();

                unknownInt2 = er.ReadUInt32();

                //offset 0xC
                topScreenDrawType = er.ReadUInt32();// 0 = none, 1 = solid colour, 2 = extension of val1, 3 = texture
                topScreenFrameType = er.ReadUInt32();//0 = solid colour(only when draw type is 1 else uses tex 0), 1 = texture0, 3 = texture1
                topScreenSolidColorDataOffset = er.ReadUInt32();
                topScreenTextureOffset = er.ReadUInt32();
                topScreenAdditionalTextureOffset = er.ReadUInt32();//used with draw type val2, optional when using draw type val2

                System.Console.WriteLine("topScreenDrawType " + topScreenDrawType);
				System.Console.WriteLine("topScreenFrameType " + topScreenFrameType);
				System.Console.WriteLine("topScreenAdditionalTextureOffset " + topScreenAdditionalTextureOffset);
				System.Console.WriteLine("topScreenSolidColorDataOffset " + topScreenSolidColorDataOffset);


                //offset 0x20
                bottomScreenDrawType = er.ReadUInt32();
                bottomScreenFrameType = er.ReadUInt32();
                bottomScreenSolidOrTextureOffset = er.ReadUInt32();

                System.Console.WriteLine("bottomScreenDrawType " + bottomScreenDrawType);
                System.Console.WriteLine("bottomScreenFrameType " + bottomScreenFrameType);
                System.Console.WriteLine("bottomScreenSolidOrTextureOffset " + bottomScreenSolidOrTextureOffset);

                //offset 0x2C
                useUnknownBlock0 = er.ReadUInt32() == 1;
                unknownBlock0Offset = er.ReadUInt32();//0xC length

                //offset 0x34
                useUnknownBlock1 = er.ReadUInt32() == 1;
                unknownBlock1Offset = er.ReadUInt32();//0xC length

                //offset 0x3C
                useFolderTextures = er.ReadUInt32() == 1;
                closedFolderTextureOffset = er.ReadUInt32();//texture 6
                openedFolderTextureOffset = er.ReadUInt32();//texture 7

                //offset 0x48
                useUnknownBlock2 = er.ReadUInt32() == 1;
                unknownBlock2Offset = er.ReadUInt32();//0xD length

                //offset 0x50
                useIconBorders = er.ReadUInt32() == 1;
                iconBorder48pxOffset = er.ReadUInt32();
                iconBorder24pxOffset = er.ReadUInt32();

                //offset 0x4C
                useUnknownBlock3 = er.ReadUInt32() == 1;
                unknownBlock3Offset = er.ReadUInt32();//0xD length

                useUnknownBlock4 = er.ReadUInt32() == 1;
                unknownBlock4Offset = er.ReadUInt32();//0x9 length

                useUnknownBlock5and6 = er.ReadUInt32() == 1;
                unknownBlock5Offset = er.ReadUInt32();//0x20 length
                unknownBlock6Offset = er.ReadUInt32();//0x20 length

                useUnknownBlock7 = er.ReadUInt32() == 1;
                unknownBlock7Offset = er.ReadUInt32();//0xD length

                useUnknownBlock8 = er.ReadUInt32() == 1;
                unknownBlock8Offset = er.ReadUInt32();//0xD length

                useUnknownBlock9 = er.ReadUInt32() == 1;
                unknownBlock9Offset = er.ReadUInt32();//0x9 length

                useUnknownBlock10 = er.ReadUInt32() == 1;
                unknownBlock10Offset = er.ReadUInt32();//0xD length

                useUnknownBlock11 = er.ReadUInt32() == 1;
                unknownBlock11Offset = er.ReadUInt32();//0x20 length

                useUnknownBlock12 = er.ReadUInt32() == 1;
                unknownBlock12Offset = er.ReadUInt32();//0x15 length

                useUnknownBlock13 = er.ReadUInt32() == 1;
                unknownBlock13Offset = er.ReadUInt32();//0xC length

                useUnknownBlock14 = er.ReadUInt32() == 1;
                unknownBlock14Offset = er.ReadUInt32();//0x6 length

                useAudioSection = er.ReadUInt32() == 1;
                audioSectionSize = er.ReadUInt32();
                audioSectionOffset = er.ReadUInt32();

                System.Console.WriteLine("useFolderTextures " + useFolderTextures);
                System.Console.WriteLine("useIconBorders " + useIconBorders);
                System.Console.WriteLine("useAudioSection " + useAudioSection);
                System.Console.WriteLine("audioSectionSize " + audioSectionSize);
                System.Console.WriteLine("audioSectionOffset " + audioSectionOffset);

            }

            public void Write(EndianBinaryWriter er)
            {
                er.BaseStream.Position = 0;
                //write header data
                er.Write(version);

                er.Write(unknownByte1);
                er.Write((byte)(backgroundMusicEnabled ? 1:0));
                er.Write(unknownByte2);
                er.Write(unknownByte3);

                er.Write(unknownInt2);

                er.Write(topScreenDrawType);
                er.Write(topScreenFrameType);
                er.Write(topScreenSolidColorDataOffset);
                er.Write(topScreenTextureOffset);
                er.Write(topScreenAdditionalTextureOffset);


                er.Write(bottomScreenDrawType);
                er.Write(bottomScreenFrameType);
                er.Write(bottomScreenSolidOrTextureOffset);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write(useFolderTextures ? 1u : 0u);//TODO write 0 instead or somethign when this is disabled?
                er.Write(closedFolderTextureOffset);
                er.Write(openedFolderTextureOffset);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write(useIconBorders ? 1u : 0u);//TODO write 0 instead or somethign when this is disabled?
                er.Write(iconBorder48pxOffset);
                er.Write(iconBorder24pxOffset);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);

                er.Write((UInt32)0);
                er.Write((UInt32)0);
                er.Write((UInt32)0);
            }

            public UInt32 version;
            public Byte unknownByte1;
            public bool backgroundMusicEnabled;
            public Byte unknownByte2;
            public Byte unknownByte3;

            public UInt32 unknownInt2;


            public UInt32 topScreenDrawType;
            public UInt32 topScreenFrameType;
            public UInt32 topScreenSolidColorDataOffset;
            public UInt32 topScreenTextureOffset;
            public UInt32 topScreenAdditionalTextureOffset;

            public UInt32 bottomScreenDrawType;
            public UInt32 bottomScreenFrameType;
            public UInt32 bottomScreenSolidOrTextureOffset;

            public bool useUnknownBlock0;
            public UInt32 unknownBlock0Offset;

            public bool useUnknownBlock1;
            public UInt32 unknownBlock1Offset;

            public bool useFolderTextures;//6 and 7
            public UInt32 closedFolderTextureOffset;
            public UInt32 openedFolderTextureOffset;

            public bool useUnknownBlock2;
            public UInt32 unknownBlock2Offset;

            public bool useIconBorders;
            public UInt32 iconBorder48pxOffset;
            public UInt32 iconBorder24pxOffset;

            public bool useUnknownBlock3;
            public UInt32 unknownBlock3Offset;

            public bool useUnknownBlock4;
            public UInt32 unknownBlock4Offset;

            public bool useUnknownBlock5and6;
            public UInt32 unknownBlock5Offset;
            public UInt32 unknownBlock6Offset;

            public bool useUnknownBlock7;
            public UInt32 unknownBlock7Offset;

            public bool useUnknownBlock8;
            public UInt32 unknownBlock8Offset;

            public bool useUnknownBlock9;
            public UInt32 unknownBlock9Offset;

            public bool useUnknownBlock10;
            public UInt32 unknownBlock10Offset;

            public bool useUnknownBlock11;
            public UInt32 unknownBlock11Offset;

            public bool useUnknownBlock12;
            public UInt32 unknownBlock12Offset;

            public bool useUnknownBlock13;
            public UInt32 unknownBlock13Offset;

            public bool useUnknownBlock14;
            public UInt32 unknownBlock14Offset;


            public Boolean useAudioSection;
            public UInt32 audioSectionSize;
            public UInt32 audioSectionOffset;
        }

        public class ThemeIdentifier : FileFormatIdentifier
        {
            public override string GetCategory()
            {
                return "3DS Themes";
            }

            public override string GetFileDescription()
            {
                return "System Theme body_LZ.bin";
            }

            public override string GetFileFilter()
            {
                return "System Menu Theme (body_LZ.bin)|body_LZ.bin";
            }

            public override Bitmap GetIcon()
            {
                return null;
            }

            public override FormatMatch IsFormat(EFEFile File)
            {
                if (File.Name.Equals("body_LZ.bin"))
                    return FormatMatch.Content;
                else
                    return FormatMatch.No;
            }

        }

    }



}
