using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using static System.Windows.Forms.ListBox;

namespace RMDEC
{
    public partial class mvProjectToolset : Form
    {
        public MVProject mvProject;

        public mvProjectToolset(MVProject proj)
        {
            mvProject = proj;
            InitializeComponent();
        }
        internal long _encryptedImageCount = 0;
        internal long _encryptedMusicCount = 0;

        private long unencryptedImageCount = 0;
        private long unencryptedMusicCount = 0;

        private List<string> encFileList = new List<string>();
        private List<string> decFileList = new List<string>();

        private string[] blacklistedFiles = { Path.Combine("icon", "icon.png"), Path.Combine("img", "system", "Window.png"), Path.Combine("img", "system", "Loading.png") };

        private bool encryptedIndexingComplete = false;
        private bool unencryptedIndexingComplete = false;



        private long encryptedImageCount {
            get
            {
                return _encryptedImageCount;
            }
            set
            {
                _encryptedImageCount = value;

                bool curent = mvProject.EncryptedImages;
                if(value <=0)
                {
                    if(curent != false)
                    {
                        mvProject.EncryptedImages = false;
                    }
                }
                else
                {
                    if(curent != true)
                    {
                        mvProject.EncryptedImages = true;
                    }
                }
                updateTitle();
            }

        }

        private long encryptedMusicCount
        {
            get
            {
                return _encryptedMusicCount;
            }
            set
            {
                _encryptedMusicCount = value;

                bool curent = mvProject.EncryptedAudio;
                if (value <= 0)
                {
                    if(curent != false)
                    {
                        mvProject.EncryptedAudio = false;
                    }
                }
                else
                {
                    if(curent != true)
                    {
                        mvProject.EncryptedAudio = true;
                    }  
                }
                updateTitle();
            }

        }


        private void onIndexThreadComplete()
        {
            if(encryptedIndexingComplete && unencryptedIndexingComplete)
            {
                processingText.Text = "Waiting";
                processingText.BackColor = Color.Red;

                decryptSelected.Enabled = true;
                encryptSelected.Enabled = true;

                makeMV.Enabled = true;
                unmakeMV.Enabled = true;

                processingBar.Style = ProgressBarStyle.Continuous;

                this.Cursor = Cursors.Arrow;
            }
        }
        private void indexFiles()
        {
            processingText.Text = "Indexing";
            processingText.BackColor = Color.Yellow;
            processingBar.Style = ProgressBarStyle.Marquee;
            this.Cursor = Cursors.WaitCursor;

            //Index Encrypted Files
            Thread indexEncryptedFilesThread = new Thread(() =>
            {

                List<string> pngList = new List<string>();
                List<string> oggList = new List<string>();
                List<string> m4aList = new List<string>();

                foreach (string file in Directory.EnumerateFiles(mvProject.FilePath, "*", SearchOption.AllDirectories))
                {


                    string fileExtension = Path.GetExtension(file).ToLower();

                    switch (fileExtension)
                    {
                        case ".rpgmvp":
                            pngList.Add(file);
                            break;
                        case ".png_":
                            pngList.Add(file);
                            break;
                        case ".rpgmvo":
                            oggList.Add(file);
                            break;
                        case ".ogg_":
                            oggList.Add(file);
                            break;
                        case ".rpgmvm":
                            m4aList.Add(file);
                            break;
                        case ".m4a_":
                            m4aList.Add(file);
                            break;
                    }

                }


                //add PNG
                foreach (string png in pngList)
                {
                    string relativeName = png.Remove(0, mvProject.FilePath.Length+1);
                    Invoke((Action)delegate
                    {
                        encryptedFileList.Items.Add("[PNG] " + relativeName);
                    });
                    encFileList.Add(png);
                }

                //add OGG
                foreach (string ogg in oggList)
                {
                    string relativeName = ogg.Remove(0, mvProject.FilePath.Length+1);
                    Invoke((Action)delegate
                    {
                        encryptedFileList.Items.Add("[OGG] " + relativeName);
                    });
                    encFileList.Add(ogg);
                }

                //add M4A
                foreach (string m4a in m4aList)
                {
                    string relativeName = m4a.Remove(0, mvProject.FilePath.Length+1);
                    Invoke((Action)delegate
                    {
                        encryptedFileList.Items.Add("[M4A] " + relativeName);
                    });
                    encFileList.Add(m4a);
                }

                Invoke((Action)delegate
                {
                    encryptedImageCount = pngList.Count;
                    encryptedMusicCount = oggList.Count + m4aList.Count;

                    encryptedIndexingComplete = true;
                    onIndexThreadComplete();
                });
                
            });
            indexEncryptedFilesThread.Start();

            //Index Unencrypted Files
            Thread indexUnencryptedFilesThread = new Thread(() =>
            {

                List<string> pngList = new List<string>();
                List<string> oggList = new List<string>();
                List<string> m4aList = new List<string>();

                foreach (string file in Directory.EnumerateFiles(mvProject.FilePath, "*", SearchOption.AllDirectories))
                {

                    string relativeName = file.Remove(0, mvProject.FilePath.Length + 1);
                    string fileExtension = Path.GetExtension(file).ToLower();

                    if(blacklistedFiles.Contains(relativeName))
                    {
                        continue;
                    }

                    switch (fileExtension)
                    {
                        case ".png":
                            pngList.Add(file);
                            break;
                        case ".ogg":
                            oggList.Add(file);
                            break;
                        case ".m4a":
                            m4aList.Add(file);
                            break;
                    }


                }

                //add PNG
                foreach (string png in pngList)
                {
                    string relativeName = png.Remove(0, mvProject.FilePath.Length+1);

                    Invoke((Action)delegate
                    {
                        decryptedFileList.Items.Add("[PNG] " + relativeName);
                    });
                    decFileList.Add(png);
                }

                //add OGG
                foreach (string ogg in oggList)
                {
                    string relativeName = ogg.Remove(0, mvProject.FilePath.Length+1);

                    Invoke((Action)delegate
                    {
                        decryptedFileList.Items.Add("[OGG] " + relativeName);
                    });
                    decFileList.Add(ogg);
                }

                //add M4A
                foreach (string m4a in m4aList)
                {
                    string relativeName = m4a.Remove(0, mvProject.FilePath.Length+1);

                    Invoke((Action)delegate
                    {
                        decryptedFileList.Items.Add("[M4A] " + relativeName);
                    });
                    decFileList.Add(m4a);
                }

                Invoke((Action)delegate
                {
                    unencryptedImageCount = pngList.Count;
                    unencryptedMusicCount = oggList.Count + m4aList.Count;

                    unencryptedIndexingComplete = true;
                    onIndexThreadComplete();
                });
            });
            indexUnencryptedFilesThread.Start();


        }
        private void MVProjectToolset_Load(object sender, EventArgs e)
        {
            updateTitle();
            indexFiles();

            if(mvProject.MZ)
            {
                unmakeMV.Text = "Deploy MZ Project";
                makeMV.Text = "Un-Deploy MZ Project";
            }
        }

        private void updateTitle()
        {
            string Engine = "[RMMV]";
            if (mvProject.MZ)
                Engine = "[RMMZ]";
            this.Text = "RMDec - "+ Engine +" " + mvProject.GameTitle + " - " + mvProject.FilePath + " - encryptedImages: "+mvProject.EncryptedImages.ToString()+" - encrypedAudio: "+mvProject.EncryptedAudio.ToString();
        }
        private string encodeFileExtension(string fileName)
        {
            string extension = Path.GetExtension(fileName).ToLower();
            string newExtension = "";

            switch (extension)
            {
                case ".png":
                    if (!mvProject.MZ)
                        newExtension = ".rpgmvp";
                    else
                        newExtension = extension + "_";
                    Invoke((Action)delegate
                    {
                        encryptedImageCount += 1;
                        unencryptedImageCount -= 1;
                    });
                    break;
                case ".ogg":
                    if (!mvProject.MZ)
                        newExtension = ".rpgmvo";
                    else
                        newExtension = extension + "_";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount += 1;
                        unencryptedMusicCount -= 1;
                    });
                    break;
                case ".m4a":
                    if (!mvProject.MZ)
                        newExtension = ".rpgmvm";
                    else
                        newExtension = extension + "_";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount += 1;
                        unencryptedMusicCount -= 1;
                    });
                    break;
            }

            return newExtension;
        }
        private void encryptSelected_Click(object sender, EventArgs e)
        {
            SelectedIndexCollection itemList = decryptedFileList.SelectedIndices;
            int itemCount = itemList.Count;

            if (itemCount < 1)
            {
                return;
            }
            
            int item = 0;

            processingBar.Style = ProgressBarStyle.Continuous;
            processingBar.Maximum = itemCount;

            processingText.BackColor = Color.Yellow;

            encryptSelected.Enabled = false;
            decryptSelected.Enabled = false;
            

            Thread decryptFilesThread = new Thread(() =>
            {
                int i = 1;
                int total = itemCount;
                do
                {

                    Invoke((Action)delegate
                    {

                        itemCount = itemList.Count;
                        item = itemList[0];

                        processingText.Text = "Encrypting " + i.ToString() + "/" + total.ToString();
                    });



                    string decryptedFile = decFileList[item];
                    string newExtension = encodeFileExtension(decryptedFile);
                    string encryptedFile = Path.ChangeExtension(decryptedFile, newExtension);

                    FileStream Decrypted = File.OpenRead(decryptedFile);
                    FileStream Encrypted = File.OpenWrite(encryptedFile);

                    mvProject.EncryptFile(Decrypted, Encrypted);

                    Encrypted.Close();
                    Decrypted.Close();

                    File.Delete(decryptedFile);

                    Invoke((Action)delegate
                    {
                        string entry = decryptedFileList.Items[item].ToString();
                        string encryptedEntry = Path.ChangeExtension(entry, newExtension);
                        encryptedFileList.Items.Add(encryptedEntry);
                        encFileList.Add(encryptedFile);

                        decryptedFileList.Items.RemoveAt(item);
                        decFileList.RemoveAt(item);

                        processingBar.Value = i;
                    });

                    i++;
                } while (itemCount > 1);

                Invoke((Action)delegate
                {
                    encryptSelected.Enabled = true;
                    decryptSelected.Enabled = true;
                    processingText.BackColor = Color.Green;
                    processingText.Text = "Encrypted " + total.ToString() + " files!";
                });
            });

            decryptFilesThread.Start();
        }

        
        private string decodeFileExtension(string fileName)
        {
            string extension = Path.GetExtension(fileName).ToLower();
            string newExtension = "";

            switch (extension)
            {
                case ".rpgmvp":
                    newExtension = ".png";
                    Invoke((Action)delegate
                    {
                        encryptedImageCount -= 1;
                        unencryptedImageCount += 1;
                    });
                    break;
                case ".rpgmvo":
                    newExtension = "ogg";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount -= 1;
                        unencryptedMusicCount += 1;
                    });
                    break;
                case ".rpgmvm":
                    newExtension = ".m4a";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount -= 1;
                        unencryptedMusicCount += 1;
                    });
                    break;
                case ".ogg_":
                    newExtension = ".ogg";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount -= 1;
                        unencryptedMusicCount += 1;
                    });
                    break;
                case ".m4a_":
                    newExtension = ".m4a";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount -= 1;
                        unencryptedMusicCount += 1;
                    });
                    break;
                case ".png_":
                    newExtension = ".png";
                    Invoke((Action)delegate
                    {
                        encryptedMusicCount -= 1;
                        unencryptedMusicCount += 1;
                    });
                    break;
            }

            return newExtension;
        }

        private void decryptSelected_Click(object sender, EventArgs e)
        {
            SelectedIndexCollection itemList = encryptedFileList.SelectedIndices;
            int itemCount = itemList.Count;

            if (itemCount < 1)
            {
                return;
            }

            int item = 0;

            processingBar.Style = ProgressBarStyle.Continuous;
            processingBar.Maximum = itemCount;

            processingText.BackColor = Color.Yellow;

            encryptSelected.Enabled = false;
            decryptSelected.Enabled = false;

            makeMV.Enabled = false;
            unmakeMV.Enabled = false;

            Thread decryptFilesThread = new Thread(() =>
            {
                int i = 1;
                int total = itemCount;
                do
                {

                    Invoke((Action)delegate
                    {

                        itemCount = itemList.Count;
                        item = itemList[0];

                        processingText.Text = "Decrypting " + i.ToString() + "/" + total.ToString();
                    });



                    string encryptedFile = encFileList[item];
                    string newExtension = decodeFileExtension(encryptedFile);
                    string decryptedFile = Path.ChangeExtension(encryptedFile, newExtension);

                    FileStream Encrypted = File.OpenRead(encryptedFile);
                    FileStream Decrypted = File.OpenWrite(decryptedFile);

                    mvProject.DecryptFile(Encrypted, Decrypted);

                    Encrypted.Close();
                    Decrypted.Close();

                    File.Delete(encryptedFile);

                    Invoke((Action)delegate
                    {
                        string entry = encryptedFileList.Items[item].ToString();
                        string decryptedEntry = Path.ChangeExtension(entry, newExtension);
                        decryptedFileList.Items.Add(decryptedEntry);
                        decFileList.Add(decryptedFile);

                        encryptedFileList.Items.RemoveAt(item);
                        encFileList.RemoveAt(item);

                        processingBar.Value = i;
                    });

                    i++;
                } while (itemCount > 1);

                Invoke((Action)delegate
                {
                    encryptSelected.Enabled = true;
                    decryptSelected.Enabled = true;
                    makeMV.Enabled = true;
                    unmakeMV.Enabled = true;
                    processingText.BackColor = Color.Green;
                    processingText.Text = "Decrypted " + total.ToString() + " files!";
                });
            });

            decryptFilesThread.Start(); 
        }

        private void onMakeMvComplete()
        {
            if(!mvProject.MZ)
            {
                string rpgProject = Path.Combine(mvProject.FilePath, "Game.rpgproject");
                File.WriteAllText(rpgProject, "RPGMV 1.0.0");
            }    
            else
            {
                string rpgProject = Path.Combine(mvProject.FilePath, "game.rmmzproject");
                File.WriteAllText(rpgProject, "RPGMZ 1.0.1");
            }


            processingText.BackColor = Color.Green;
            processingText.Text = "Game Un-Deployed!";

            encryptSelected.Enabled = true;
            decryptSelected.Enabled = true;
            makeMV.Enabled = true;
            unmakeMV.Enabled = true;

            Process.Start(mvProject.FilePath+"\\");
        }
        private void makeMV_Click(object sender, EventArgs e)
        {
            int itemCount = encFileList.Count;

            //Decrypt Everything
            if (itemCount > 0)
            {
                processingBar.Style = ProgressBarStyle.Continuous;
                processingBar.Maximum = itemCount;

                processingText.BackColor = Color.Yellow;

                encryptSelected.Enabled = false;
                decryptSelected.Enabled = false;

                makeMV.Enabled = false;
                unmakeMV.Enabled = false;

                Thread decryptFilesThread = new Thread(() =>
                {
                   
                    for (int i = 0; i < itemCount; i++)
                    {
                        Invoke((Action)delegate
                        {
                            processingText.Text = "Decrypting " + i.ToString() + "/" + itemCount.ToString();
                        });

                        
                        string encryptedFile = encFileList[0];
                        string newExtension = decodeFileExtension(encryptedFile);
                        string decryptedFile = Path.ChangeExtension(encryptedFile, newExtension);

                        FileStream Encrypted = File.OpenRead(encryptedFile);
                        FileStream Decrypted = File.OpenWrite(decryptedFile);

                        mvProject.DecryptFile(Encrypted, Decrypted);

                        Encrypted.Close();
                        Decrypted.Close();

                        File.Delete(encryptedFile);

                        Invoke((Action)delegate
                        {
                            string entry = encryptedFileList.Items[0].ToString();
                            string decryptedEntry = Path.ChangeExtension(entry, newExtension);
                            decryptedFileList.Items.Add(decryptedEntry);
                            decFileList.Add(decryptedFile);

                            encryptedFileList.Items.RemoveAt(0);
                            encFileList.RemoveAt(0);

                            processingBar.Value = i;
                        });
                    }


                    
                    Invoke((Action)delegate
                    {
                        onMakeMvComplete();
                    });

                    

                });
                decryptFilesThread.Start();
            }
            else
            {
                onMakeMvComplete();
            }
        }

        private void onUnMakeMvComplete()
        {
            string rpgProject = Path.Combine(mvProject.FilePath, "Game.rpgproject");
            if (mvProject.MZ)
                rpgProject = Path.Combine(mvProject.FilePath, "game.rmmzproject");
            if (File.Exists(rpgProject))
            {
                File.Delete(rpgProject);
            }

            processingText.BackColor = Color.Green;
            processingText.Text = "Game Deployed!";

            encryptSelected.Enabled = true;
            decryptSelected.Enabled = true;
            makeMV.Enabled = true;
            unmakeMV.Enabled = true;
      
        }
        private void unmakeMV_Click(object sender, EventArgs e)
        {
            int itemCount = decFileList.Count;

            //Encrypt Everything
            if (itemCount > 0)
            {
                processingBar.Style = ProgressBarStyle.Continuous;
                processingBar.Maximum = itemCount;

                processingText.BackColor = Color.Yellow;

                encryptSelected.Enabled = false;
                decryptSelected.Enabled = false;

                makeMV.Enabled = false;
                unmakeMV.Enabled = false;

                Thread encryptFilesThread = new Thread(() =>
                {

                    for (int i = 0; i < itemCount; i++)
                    {
                        Invoke((Action)delegate
                        {
                            processingText.Text = "Encrypting " + i.ToString() + "/" + itemCount.ToString();
                        });


                        string decryptedFile = decFileList[0];
                        string newExtension = encodeFileExtension(decryptedFile);
                        string encryptedFile = Path.ChangeExtension(decryptedFile, newExtension);

                        FileStream Encrypted = File.OpenWrite(encryptedFile);
                        FileStream Decrypted = File.OpenRead(decryptedFile);

                        mvProject.EncryptFile(Decrypted,Encrypted);

                        Encrypted.Close();
                        Decrypted.Close();

                        File.Delete(decryptedFile);

                        Invoke((Action)delegate
                        {
                            string entry = decryptedFileList.Items[0].ToString();
                            string encryptedEntry = Path.ChangeExtension(entry, newExtension);
                            encryptedFileList.Items.Add(encryptedEntry);
                            encFileList.Add(encryptedFile);

                            decryptedFileList.Items.RemoveAt(0);
                            decFileList.RemoveAt(0);

                            processingBar.Value = i;
                        });
                    }



                    Invoke((Action)delegate
                    {
                        onUnMakeMvComplete();
                    });

                });
                encryptFilesThread.Start();
            }
            else
            {
                onUnMakeMvComplete();
            }
        }

        private void MVProjectToolset_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(mvProject.EncryptedAudio == true && unencryptedMusicCount > 0)
            {
                DialogResult res = MessageBox.Show("Warning: You have mixed unencrypted/encrypted Audio files!\n\nMV Requires ALL Decrypted or ALL Encrypted\nThe game WILL CRASH when attemping to load these files\nPlease Encrypt ALL Audio Files or Decrypt ALL Audio Files.", "Mixed Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
                if(res == DialogResult.OK)
                {
                    e.Cancel = true;
                }
            }
            if (mvProject.EncryptedImages == true && unencryptedImageCount > 0)
            {
                DialogResult res = MessageBox.Show("Warning: You have mixed unencrypted/encrypted Image files!\n\nMV Requires ALL Decrypted or ALL Encrypted\nThe game WILL CRASH when attemping to load these files\nPlease Encrypt ALL Images or Decrypt ALL Images.", "Mixed Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
                if (res == DialogResult.OK)
                {
                    e.Cancel = true;
                }
            }
        }

      
    }
}
