// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;
using System.Linq;
using System.Threading;

namespace WPinternals
{
    internal class LumiaUnlockRootViewModel: ContextViewModel
    {
        private PhoneNotifierViewModel PhoneNotifier;
        private Action SwitchToUnlockBoot;
        private Action SwitchToDumpRom;
        private Action SwitchToFlashRom;
        private Action Callback;
        private bool DoUnlock;

        internal LumiaUnlockRootViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToDumpRom, Action SwitchToFlashRom, bool DoUnlock, Action Callback)
            : base()
        {
            IsSwitchingInterface = false;
            IsFlashModeOperation = true;

            this.PhoneNotifier = PhoneNotifier;
            this.SwitchToDumpRom = SwitchToDumpRom;
            this.SwitchToFlashRom = SwitchToFlashRom;
            this.SwitchToUnlockBoot = SwitchToUnlockBoot;
            this.DoUnlock = DoUnlock;
            this.Callback = Callback;
        }

        internal override void EvaluateViewState()
        {
            if (!IsActive)
                return;

            if (DoUnlock)
            {
                if ((SubContextViewModel == null) || (SubContextViewModel is LumiaUndoRootTargetSelectionViewModel))
                    ActivateSubContext(new LumiaUnlockRootTargetSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpRom, SwitchToFlashRom, DoUnlockPhone, DoUnlockImage));
            }
            else
            {
                if ((SubContextViewModel == null) || (SubContextViewModel is LumiaUnlockRootTargetSelectionViewModel))
                    ActivateSubContext(new LumiaUndoRootTargetSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToDumpRom, SwitchToFlashRom, DoUnlockPhone, DoUnlockImage));
            }
        }

        internal async void DoUnlockPhone()
        {
            try
            {
                IsSwitchingInterface = true;
                await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_MassStorage,
                    (msg, sub) =>
                        ActivateSubContext(new BusyViewModel(msg, sub)));
                bool HasNewBootloader = HasNewBootloaderFromMassStorage();
                string EFIESPPath = HasNewBootloader ? null : ((MassStorage)PhoneNotifier.CurrentModel).Drive + @"\EFIESP\";
                string MainOSPath = ((MassStorage)PhoneNotifier.CurrentModel).Drive + @"\";
                StartPatch(EFIESPPath, MainOSPath, HasNewBootloader);
            }
            catch (Exception Ex)
            {
                ActivateSubContext(new MessageViewModel(Ex.Message, () => {
                    Callback();
                    ActivateSubContext(null);
                }));
            }
        }

        internal void DoUnlockImage(string EFIESPMountPoint, string MainOSMountPoint)
        {
            StartPatch(EFIESPMountPoint, MainOSMountPoint, false); // Unlock image is only supported for Lumia's with bootloader Spec A. Due to complexity of Spec B bootloader hack, it cannot be applied on a mounted image.
        }

        // Magic!
        // Apply patches for Root Access
        private void StartPatch(string EFIESP, string MainOS, bool HasNewBootloader)
        {
            IsSwitchingInterface = false;
            new Thread(() =>
                {
                    if (DoUnlock)
                        LogFile.BeginAction("EnableRootAccess");
                    else
                        LogFile.BeginAction("DisableRootAccess");

                    bool Result = false;

                    if (EFIESP != null)
                    {
                        if (DoUnlock)
                            ActivateSubContext(new BusyViewModel("Enable Root Access on EFIESP..."));
                        else
                            ActivateSubContext(new BusyViewModel("Disable Root Access on EFIESP..."));

                        try
                        {
                            App.PatchEngine.TargetPath = EFIESP;
                            if (DoUnlock)
                            {
                                Result = App.PatchEngine.Patch("SecureBootHack-V1-EFIESP");

                                if (!Result)
                                {
                                    ActivateSubContext(new MessageViewModel("Failed to enable Root Access on EFIESP! Check the OS version on the phone and verify the compatibility-list in the \"Getting started\" section.", Exit));
                                    return;
                                }
                            }
                            else
                            {
                                App.PatchEngine.Restore("SecureBootHack-V1-EFIESP");
                                Result = true;
                            }
                        }
                        catch (UnauthorizedAccessException Ex)
                        {
                            LogFile.LogException(Ex);
                            ActivateSubContext(new MessageViewModel("Failed to enable Root Access on EFIESP! Not enough privileges to perform action. Try to logon to Windows with an administrator account.", Exit));
                            return;
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                            Result = false;
                        }

                        if (!Result)
                        {
                            ActivateSubContext(new MessageViewModel("Failed to enable Root Access on EFIESP!", Exit));
                            return;
                        }
                    }

                    if (MainOS != null)
                    {
                        if (DoUnlock)
                            ActivateSubContext(new BusyViewModel("Enable Root Access on MainOS..."));
                        else
                            ActivateSubContext(new BusyViewModel("Disable Root Access on MainOS..."));

                        try
                        {
                            App.PatchEngine.TargetPath = MainOS;
                            if (DoUnlock)
                            {
                                Result = App.PatchEngine.Patch("RootAccess-MainOS");

                                if (Result)
                                    Result = App.PatchEngine.Patch("SecureBootHack-MainOS");

                                if (!Result)
                                {
                                    ActivateSubContext(new MessageViewModel("Failed to enable Root Access on MainOS! Check the OS version on the phone and verify the compatibility-list in the \"Getting started\" section.", Exit));
                                    return;
                                }
                            }
                            else
                            {
                                App.PatchEngine.Restore("RootAccess-MainOS");

                                if (!HasNewBootloader)
                                    App.PatchEngine.Restore("SecureBootHack-MainOS");

                                Result = true;
                            }
                        }
                        catch (UnauthorizedAccessException Ex)
                        {
                            LogFile.LogException(Ex);
                            ActivateSubContext(new MessageViewModel("Failed to enable Root Access on MainOS! Not enough privileges to perform action. Try to logon to Windows with an administrator account.", Exit));
                            Result = false;
                        }
                        catch (Exception Ex)
                        {
                            LogFile.LogException(Ex);
                            Result = false;
                        }

                        if (!Result)
                        {
                            ActivateSubContext(new MessageViewModel("Failed to enable Root Access on MainOS!", Exit));
                        }
                        else
                        {
                            if (DoUnlock)
                                ActivateSubContext(new MessageViewModel("Root Access successfully enabled!", Exit));
                            else
                                ActivateSubContext(new MessageViewModel("Root Access successfully disabled!", Exit));
                        }
                    }

                    if (DoUnlock)
                        LogFile.EndAction("EnableRootAccess");
                    else
                        LogFile.EndAction("DisableRootAccess");
                }).Start();
        }

        private void Exit()
        {
            IsSwitchingInterface = false;
            Callback();
            ActivateSubContext(null);
        }

        private bool HasNewBootloaderFromMassStorage()
        {
            bool Result = false;
            MassStorage Phone = (MassStorage)PhoneNotifier.CurrentModel;
            Phone.OpenVolume(false);
            byte[] GPTBuffer = Phone.ReadSectors(1, 33);
            GPT GPT = new WPinternals.GPT(GPTBuffer);
            Partition Partition = GPT.GetPartition("UEFI");
            byte[] UefiBuffer = Phone.ReadSectors(Partition.FirstSector, Partition.LastSector - Partition.FirstSector + 1);
            UEFI UEFI = new UEFI(UefiBuffer);
            string BootMgrName = UEFI.EFIs.Where(efi => ((efi.Name != null) && ((efi.Name.Contains("BootMgrApp")) || (efi.Name.Contains("FlashApp"))))).First().Name;
            byte[] BootMgr = UEFI.GetFile(BootMgrName);
            // "Header V2"
            Result = (ByteOperations.FindAscii(BootMgr, "Header V2") != null);
            Phone.CloseVolume();
            return Result;
        }
    }
}
