/***************************************************************************
 *  Copyright (C) 2013 by Peter L Jones                                    *
 *  pljones@users.sf.net                                                   *
 *                                                                         *
 *  This file is part of the Sims 3 Package Interface (s3pi)               *
 *                                                                         *
 *  s3pi is free software: you can redistribute it and/or modify           *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation, either version 3 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  s3pi is distributed in the hope that it will be useful,                *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with s3pi.  If not, see <http://www.gnu.org/licenses/>.          *
 ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using s3pi.Interfaces;
using s3pi.Package;
using s3pi.Extensions;
using System.Text;

namespace S3PIDemoFE
{
    public interface IBuiltInValueControl
    {
        bool IsAvailable { get; }
        Control ValueControl { get; }
        IEnumerable<ToolStripItem> GetContextMenuItems(EventHandler cbk);
    }

    public abstract class ABuiltInValueControl : IBuiltInValueControl
    {
        public abstract bool IsAvailable { get; }
        public abstract Control ValueControl { get; }
        public abstract IEnumerable<ToolStripItem> GetContextMenuItems(EventHandler cbk);

        protected ABuiltInValueControl(Stream s) { }

        static List<KeyValuePair<List<uint>, Type>> builtInValueControlLookup = new List<KeyValuePair<List<uint>, Type>>();
        static ABuiltInValueControl()
        {
            var types = typeof(ABuiltInValueControl).Assembly.GetTypes().Where(t => !t.IsAbstract && typeof(ABuiltInValueControl).IsAssignableFrom(t));
            foreach (var t in types)
            {
                var fi = t.GetField("resourceTypes", BindingFlags.NonPublic | BindingFlags.Static);
                if (fi == null)
                    continue;
                if (!fi.FieldType.HasElementType || fi.FieldType.GetElementType() != typeof(uint))
                    continue;
                builtInValueControlLookup.Add(new KeyValuePair<List<uint>, Type>(
                   new List<uint>((uint[])fi.GetValue(fi)), t));
            }
        }

        public static bool Exists(uint resourceType)
        {
            return builtInValueControlLookup
                .Where(x => x.Key.Contains(resourceType))
                .Select(x => Activator.CreateInstance(x.Value, new object[] { Stream.Null, }) as ABuiltInValueControl)
                .Where(x => x.IsAvailable)
                .FirstOrDefault() != null;
        }

        public static IBuiltInValueControl Lookup(uint resourceType, Stream s)
        {
            return builtInValueControlLookup
                .Where(x => x.Key.Contains(resourceType))
                .Select(x => Activator.CreateInstance(x.Value, new object[] { s, }) as ABuiltInValueControl)
                .Where(x => x.IsAvailable)
                .FirstOrDefault();
        }
    }

    class DDSControl : ABuiltInValueControl
    {
        static uint[] resourceTypes = new uint[] {
            0x00B2D882,
            0x8FFB80F6,
        };

        static bool channel1 = true, channel2 = true, channel3 = true, channel4 = true, invertch4 = false;
        Stream resStream;
        DDSPanel ddsPanel1;
        public DDSControl(Stream s)
            : base(s)
        {
            if (s == null || s == Stream.Null)
                return;

            resStream = s;
            ddsPanel1 = new DDSPanel()
            {
                Fit = true,
                Channel1 = channel1,
                Channel2 = channel2,
                Channel3 = channel3,
                Channel4 = channel4,
                InvertCh4 = invertch4,
                Margin = new Padding(3),
            };
            ddsPanel1.Channel1Changed += (sn, e) => channel1 = ddsPanel1.Channel1;
            ddsPanel1.Channel2Changed += (sn, e) => channel2 = ddsPanel1.Channel2;
            ddsPanel1.Channel3Changed += (sn, e) => channel3 = ddsPanel1.Channel3;
            ddsPanel1.Channel4Changed += (sn, e) => channel4 = ddsPanel1.Channel4;
            ddsPanel1.InvertCh4Changed += (sn, e) => invertch4 = ddsPanel1.InvertCh4;
            ddsPanel1.DDSLoad(resStream);
        }

        public override bool IsAvailable { get { return S3PIDemoFE.Properties.Settings.Default.EnableDDSPreview; } }

        public override Control ValueControl { get { return ddsPanel1; } }

        public override IEnumerable<ToolStripItem> GetContextMenuItems(EventHandler cbk)
        {
            const string ddsFiles = "DDS Images|*.dds";

            yield return new ToolStripSeparator();

            yield return new ToolStripMenuItem("Load DDS...", null, (s, e) => { if (getFilename("Load DDS...", ddsFiles).Any(loadDDS)) { cbk(s, e); } });
            yield return new ToolStripMenuItem("Load Image...", null, (s, e) => { if (getFilename("Load Image...", "Image files|*.png;*.gif;*.jpg;*.bmp").Any(loadImage)) { cbk(s, e); } });
            yield return new ToolStripMenuItem("Save DDS...", null, (s, e) => { if (getFilename("Save DDS...", ddsFiles, true).Any(saveDDS)) { } });
            yield return new ToolStripMenuItem("Save Image...", null, (s, e) => { if (getFilename("Save Image...", "Portable Network Grapics files|*.png|Grapics Interchange Format files|*.gif|JPEG files|*.jpg|Bitmap files|*.bmp", true).Any(saveImage)) { } });
        }

        bool loadDDS(string filename)
        {
            using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
            {
                ddsPanel1.DDSLoad(fs, true);
                fs.Close();
            }
            ddsPanel1.Channel1 = channel1;
            ddsPanel1.Channel2 = channel2;
            ddsPanel1.Channel3 = channel3;
            ddsPanel1.Channel4 = channel4;
            return true;
        }

        bool loadImage(string filename)
        {
            ddsPanel1.Import(filename, true);
            ddsPanel1.Channel1 = channel1;
            ddsPanel1.Channel2 = channel2;
            ddsPanel1.Channel3 = channel3;
            ddsPanel1.Channel4 = channel4;
            return true;
        }

        bool saveDDS(string filename)
        {
            using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
            {
                ddsPanel1.DDSSave(fs);
                fs.Close();
            }
            CopyableMessageBox.Show("Saved.", "Save DDS...");
            return true;
        }

        static System.Drawing.Imaging.ImageFormat[] fmts = new[] {
                System.Drawing.Imaging.ImageFormat.Png,
                System.Drawing.Imaging.ImageFormat.Gif,
                System.Drawing.Imaging.ImageFormat.Jpeg,
                System.Drawing.Imaging.ImageFormat.Bmp,
            };
        bool saveImage(string filename)
        {
            var ext = Array.IndexOf(new[] { ".png", ".gif", ".jpg", ".bmp", }, Path.GetExtension(filename).ToLower());
            var fmt = ext >= 0 ? fmts[ext] : System.Drawing.Imaging.ImageFormat.Png;

            ddsPanel1.Image.Save(filename, fmt);
            CopyableMessageBox.Show("Saved.", "Save Image...");
            return true;
        }

        IEnumerable<string> getFilename(string Title, string Filter, bool save = false)
        {
            FileDialog fd = save ? (FileDialog)new SaveFileDialog() : (FileDialog)new OpenFileDialog();
            fd.Title = Title;
            fd.FileName = "";
            fd.FilterIndex = 0;
            fd.Filter = Filter + "|All files|*.*";
            DialogResult dr = fd.ShowDialog();
            if (dr != DialogResult.OK) yield break;
            yield return fd.FileName;
        }
    }


    class ImageControl : ABuiltInValueControl
    {
        //TODO: static constructor read this from file
        //TODO: temporarily use the one from s3pi ImageResource wrapper source
        static uint[] resourceTypes = new uint[] {
            0x0580A2B4, //  THUM   .png
            0x0580A2B5, //  THUM   .png
            0x0580A2B6, //  THUM   .png
            0x0580A2CD, //  SNAP   .png
            0x0580A2CE, //  SNAP   .png
            0x0580A2CF, //  SNAP   .png
            0x0589DC44, //  THUM   .png
            0x0589DC45, //  THUM   .png
            0x0589DC46, //  THUM   .png
            0x0589DC47, //  THUM   .png
            0x05B17698, //  THUM   .png
            0x05B17699, //  THUM   .png
            0x05B1769A, //  THUM   .png
            0x05B1B524, //  THUM   .png
            0x05B1B525, //  THUM   .png
            0x05B1B526, //  THUM   .png
            0x0668F635, //  TWNI   .png
            0x2653E3C8, //  THUM   .png
            0x2653E3C9, //  THUM   .png
            0x2653E3CA, //  THUM   .png
            0x2D4284F0, //  THUM   .png
            0x2D4284F1, //  THUM   .png
            0x2D4284F2, //  THUM   .png
            0x2E75C764, //  ICON   .png
            0x2E75C765, //  ICON   .png
            0x2E75C766, //  ICON   .png
            0x2E75C767, //  ICON   .png
            0x2F7D0002, //  IMAG   .jpg
            0x2F7D0004, //  IMAG   .png
            0x5DE9DBA0, //  THUM   .png
            0x5DE9DBA1, //  THUM   .png
            0x5DE9DBA2, //  THUM   .png
            0x626F60CC, //  THUM   .png
            0x626F60CD, //  THUM   .png
            0x626F60CE, //  THUM   .png
            0x6B6D837D, //  SNAP   .png
            0x6B6D837E, //  SNAP   .png
            0x6B6D837F, //  SNAP   .png
            0xAD366F95, //  THUM   .png
            0xAD366F96, //  THUM   .png
            0xD84E7FC5, //  ICON   .png
            0xD84E7FC6, //  ICON   .png
            0xD84E7FC7, //  ICON   .png
            0xFCEAB65B, //  THUM   .png
        };

        PictureBox pb = new PictureBox();

        public ImageControl(Stream s)
            : base(s)
        {
            if (s == null || s == Stream.Null)
                return;

            pb.Image = Image.FromStream(s);
        }

        public override bool IsAvailable
        {
            get { return true; }
        }

        public override Control ValueControl
        {
            get { return pb; }
        }

        public override IEnumerable<ToolStripItem> GetContextMenuItems(EventHandler cbk)
        {
            yield break;
        }
    }


    //Used directly by MainForm, so needs to be public
    public class TextControl : ABuiltInValueControl
    {
        //TODO: static constructor read this from file
        //TODO: temporarily use the one from s3pi TextResource wrapper source
        static uint[] resourceTypes = new uint[] {
            0x024A0E52, // Spore UTF-8 with encoding bytes NameGeneration.package
            0x025C90A6, // Cascading Style Sheet
            0x025C95B6, // XML with encoding bytes -- <graph/>
            0x029E333B, // Audio controllers
            0x02C9EFF2, // Audio Submix
            0x024A0E52, // SimCity5 Config
            0x02FAC0B6, // Spore "Guide notes" from Text.package?
            0x0333406C, // XML with or without encoding bytes -- <base/> (with), various (without)
            0x03B33DDF, // XML without encoding bytes -- <base/>
            0x0604ABDA, // XML with encoding bytes -- <DreamTree/>
            0x0A98EAF0, // SimCity5 UI JavaScript Object
            0x1F886EAD, // Various configuration bits
            0x2B6CAB5F, // Spore Note in UI.package
            0x67771F5C, // SimCity5 UI JavaScript Object
            0x73E93EEB, // XML without encoding bytes -- <manifest/>
            0xA8D58BE5, // XML without encoding bytes -- <skills_store/>
            0xD4D9FBE5, // XML without encoding bytes -- <patternlist/>
            0xDD3223A7, // XML without encoding bytes -- <buff_store/>
            0xDD6233D6, // SimCity5 UI HTML
            0xE5105066, // ?
            0xE5105067, // XML without encoding bytes -- <RecipeMasterList_store/>
            0xE5105068, // ?
            0xF0FF5598, // Shortcut definitions
        };

        RichTextBox rtb = new RichTextBox()
        {
            Font = new Font(FontFamily.GenericMonospace, 8),
            Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top,
            ReadOnly = true,
        };

        public TextControl(Stream s)
            : base(s)
        {
            if (s == null || s == Stream.Null)
                return;

            rtb.Text = new StreamReader(s).ReadToEnd();
        }

        public TextControl(string s)
            : base(null)
        {
            if (s == null)
                return;

            rtb.Text = s;
        }

        public override bool IsAvailable { get { return true; } }

        public override Control ValueControl { get { return rtb; } }

        public override IEnumerable<ToolStripItem> GetContextMenuItems(EventHandler cbk) { yield break; }
    }

    //Used directly by MainForm, so needs to be public
    public class HexControl : ABuiltInValueControl
    {
        int rowLength = 16;

        RichTextBox rtb = new RichTextBox()
        {
            Font = new Font(FontFamily.GenericMonospace, 10),
            Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top,
            ReadOnly = true,
        };
 
        public HexControl(Stream s)
            : base(s)
        {
            if (s == null || s == Stream.Null)
                return;

            rtb.Text = GetHex(s);
        }

        public override bool IsAvailable { get { return true; } }

        public override Control ValueControl { get { return rtb; } }

        public override IEnumerable<ToolStripItem> GetContextMenuItems(EventHandler cbk) { yield break; }

        private String GetHex(Stream s)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            byte[] b = new byte[s.Length];
            s.Read(b, 0, b.Length);

            int padLength = 8;// +b.Length.ToString("X").Length;
            string rowFmt = "X" + padLength;

            sb.Append("".PadLeft(padLength + 2));
            for (int col = 0; col < rowLength; col++) sb.Append(col.ToString("X2") + " ");
            sb.AppendLine();
            sb.Append("".PadLeft(padLength + 2));
            for (int col = 0; col < rowLength; col++) sb.Append("---");
            sb.AppendLine();

            for (int row = 0; row < b.Length; row += rowLength)
            {
                sb.Append(row.ToString(rowFmt) + ": ");

                int col = 0;
                for (; col < rowLength && row + col < b.Length; col++) sb.Append(b[row + col].ToString("X2") + " ");
                for (; col < rowLength; col++) sb.Append("   ");

                sb.Append(" : ");
                for (col = 0; col < rowLength && row + col < b.Length; col++)
                    sb.Append(b[row + col] < 0x20 || b[row + col] > 0x7e ? '.' : (char)b[row + col]);

                sb.AppendLine();
            }

            return sb.ToString();
        }
    }
}