
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace TwoMbit
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        SettingsWindow settingswindow;
        LoaderWindow loaderwindow;
        App app;
        Config cfg;
        Language lang;
        Recent recent = null;
        Resolution resolution;
        bool FromFullscreen;

        const int WM_SYSCOMMAND = 0x112;
        const int SC_SCREENSAVE = 0xF140;
        const int SC_MONITORPOWER = 0xF170;

        public bool BackFromFullscreen() {
            return FromFullscreen;
        }

        public enum ItemVisibillity { LOAD, UNLOAD };

        public MainWindow(App _app, Config _cfg, Language _lang)
        {
            InitializeComponent();
            settingswindow = null;
            app = _app;
            cfg = _cfg;
            lang = _lang;

            generateRecentList();
            generateResolutionList();
            generateSpeedList();
            prepareLoadSubMenu();
            setConfig();
            translate();
            FromFullscreen = false;

            SourceInitialized += delegate {
                HwndSource source = (HwndSource)
                PresentationSource.FromVisual(this);
                source.AddHook(Hook);
            };
        }

        static IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            if (msg == WM_SYSCOMMAND &&
                ((((long)wParam & 0xFFF0) == SC_SCREENSAVE) ||
                ((long)wParam & 0xFFF0) == SC_MONITORPOWER))
                    handled = true;
            return IntPtr.Zero;
        }

        void prepareLoadSubMenu() {
            if (!App.isSystemInitialized(Cart._System.SNES)) {
                load_snes.Visibility = Visibility.Collapsed;
            }
        }

        private void checkItem(MenuItem item) {
            Image icon;
            icon = new Image();
            icon.Source = new BitmapImage(new Uri("images/check.png", UriKind.Relative));
            icon.Height = 15;
            item.Icon = icon;
        }

        public void translateAll()
        {
            translate();
            if (settingswindow != null) {
                settingswindow.translate();
            }
            if (loaderwindow != null) {
                loaderwindow.translate();
            }
        }

        public void translate()
        {
            //main menu
            file.Header = lang.get("file");
            video.Header = lang.get("video");
            config.Header = lang.get("config");
            help.Header = lang.get("help");
            //sub menu: file
            load_sms.Header = lang.get("load") + " Sms ";
            load_snes.Header = lang.get("load") + " Snes ";
            unload.Header = lang.get("unload");
            load_state.Header = lang.get("load_state");
            save_state.Header = lang.get("save_state");
            change_slot.Header = lang.get("change_slot");
            cheat_codes.Header = lang.get("cheat_codes");
            power.Header = lang.get("power");
            reset.Header = lang.get("reset");
            recent_roms.Header = lang.get("recent_roms");
            exit.Header = lang.get("exit");
            //sub menu: video
            window_size.Header = lang.get("window_size");
            fullscreen.Header = lang.get("fullscreen_resolution");
            set_fullscreen.Header = lang.get("fullscreen");
            region.Header = lang.get("region");
            video_filter.Header = lang.get("video_filter");
            vsync.Header = lang.get("vsync");
            adjust_video.Header = lang.get("adjust_video");
            //sub menu: config
            set_input.Header = lang.get("set_input");
            set_paths.Header = lang.get("set_paths");
            adjust_audio.Header = lang.get("adjust_audio");
            set_speed.Header = lang.get("set_speed");
            show_fps.Header = lang.get("show_fps");
            audio_active.Header = lang.get("audio_active");
            //sub menu: help
            about.Header = lang.get("about");
            //recent
            lock_recent.Header = lang.get("lock");
            clear_recent.Header = lang.get("clear");
        }

        protected internal void destroySettingsWindow()
        {
            settingswindow = null;
        }

        void openSettingWindow(object sender, RoutedEventArgs e)
        {
            if (settingswindow == null) {
                settingswindow = new SettingsWindow(this, app, lang, cfg);
            }

            if (settingswindow.Visibility == Visibility.Visible) {
                settingswindow.WindowState = WindowState.Normal;
            } else {
                settingswindow.Show();
            }
            settingswindow.selectView(sender, e);
        }

        void showHelp(object sender, RoutedEventArgs e) {
            MessageBox.Show(lang.get("about_desc"), lang.get("about"));
        }

        void setRegion(object sender, RoutedEventArgs e) {
            MenuItem mi = (MenuItem)sender;
            cfg.set<int>("video_region", region.Items.IndexOf(mi));
            updateRegion();
        }

        void updateRegion() {
            int count = 0;
            foreach (MenuItem item in region.Items) {
                item.Icon = null;
                if (cfg.get<int>("video_region") == count) {
                    checkItem(item);
                }
                count++;
            }
        }

        void setVsync(object sender, RoutedEventArgs e) {
            cfg.toggle("video_vsync");
            updateMenuItemCheck((MenuItem)sender, "video_vsync");
            app.updateVideo();
        }

        void setFps(object sender, RoutedEventArgs e) {
            cfg.toggle("show_fps");
            updateMenuItemCheck((MenuItem)sender, "show_fps");
            app.updateFpsCounter();
        }

        void setAudio(object sender, RoutedEventArgs e) {
            cfg.toggle("audio_active");
            updateMenuItemCheck((MenuItem)sender, "audio_active");
            app.updateAudio();
        }

        void setHWFilter(object sender, RoutedEventArgs e) {
            MenuItem mi = (MenuItem)sender;
            cfg.set<int>("video_filter_hw", video_filter.Items.IndexOf(mi));
            updateHWFilter();
        }

        void updateHWFilter() {
            video_filter_hw_point.Icon = null;
            video_filter_hw_linear.Icon = null;
            int pos = cfg.get<int>("video_filter_hw");
            if (pos == 0) {
                checkItem(video_filter_hw_point);
            } else if (pos == 1) {
                checkItem(video_filter_hw_linear);
            }
            app.updateHwFilter();
        }

        void setSWFilter(object sender, RoutedEventArgs e) {
            MenuItem mi = (MenuItem)sender;
            cfg.set<int>("video_filter_sw", video_filter.Items.IndexOf(mi) - 3);
            updateSWFilter();
        }

        void updateSWFilter() {
            video_filter_sw_normal.Icon = null;
            video_filter_sw_ntsc.Icon = null;
            int pos = cfg.get<int>("video_filter_sw");
            if (pos == 0) {
                checkItem(video_filter_sw_normal);
            } else if (pos == 1) {
                checkItem(video_filter_sw_ntsc);
            }
            app.updateSwFilter();
        }

        void updateSpeed() {
            int count = 0;

            foreach (MenuItem item in set_speed.Items) {
                item.Icon = null;
                if (cfg.get<int>("speed_select") == count) {
                    checkItem(item);
                }
                count++;
            }
            app.updateAudio();
        }

        void setSpeed(object sender, RoutedEventArgs e) {
            MenuItem mi = (MenuItem)sender;
            cfg.set<int>("speed_select", set_speed.Items.IndexOf(mi));
            updateSpeed();
        }

        void generateSpeedList() {
            Settings[] speedList = cfg.getSpeedList();
            MenuItem mi;

            foreach (Settings setting in speedList) {
                mi = new MenuItem();
                mi.Header = setting.data.ToString() + " %";
                mi.Style = FindResource("mitem") as Style;
                mi.Click += new RoutedEventHandler(setSpeed);
                set_speed.Items.Add(mi);
            }
        }

        void generateResolutionList() {
            Settings[] windowsResolutions = cfg.getWindowList();
            Settings[] fullscreenResolutions = cfg.getFullscreenList();
            MenuItem mi;
            resolution = new Resolution(this, cfg, windowsResolutions, fullscreenResolutions);

            foreach(Settings setting in windowsResolutions) {
                mi = new MenuItem();
                mi.Header = setting.str_data;
                mi.Style = FindResource("mitem") as Style;
                mi.Click += new RoutedEventHandler(setWindowResolution);
                window_size.Items.Add(mi);
            }
            MenuItem fullscreenSwitch = new MenuItem();
            fullscreenSwitch = set_fullscreen;

            fullscreen.Items.Clear();
            foreach (Settings setting in fullscreenResolutions) {
                mi = new MenuItem();
                mi.Header = setting.str_data;
                mi.Style = FindResource("mitem") as Style;
                mi.Click += new RoutedEventHandler(setFullscreenResolution);
                fullscreen.Items.Add(mi);
            }

            fullscreen.Items.Add(fullscreenSwitch);
        }

        void updateFullscreenResolution() {
            int count = 0;

            foreach (MenuItem item in fullscreen.Items) {
                item.Icon = null;
                if (cfg.get<int>("video_mode_fullscreen") == count) {
                    checkItem(item);
                }
                count++;
            }
        }

        void updateWindowsResolution() {
            int count = 0;

            foreach(MenuItem item in window_size.Items) {
                item.Icon = null;
                if (cfg.get<int>("video_mode_window") == count) {
                    checkItem(item);
                }
                count++;
            }
            FromFullscreen = false;
            resizeWindow();
            app.updateVideo();
        }
        
        void setWindowResolution(object sender, RoutedEventArgs e) {
            MenuItem mi = (MenuItem)sender;
            cfg.set<int>("video_mode_window", window_size.Items.IndexOf(mi));
            updateWindowsResolution();
        }

        void setFullscreenResolution(object sender, RoutedEventArgs e){
            MenuItem mi = (MenuItem)sender;
            cfg.set<int>("video_mode_fullscreen", fullscreen.Items.IndexOf(mi));
            updateFullscreenResolution();
        }

        void setFullscreen(object sender, RoutedEventArgs e) {}

        void toggleFullscreenCanExecute(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = true;
            e.Handled = true;
        }

        void pauseEmulation() {
            app.pauseEmu();
        }

        void unpauseEmulation() {
            app.unpauseEmu();
        }

        void minimizer(object sender, EventArgs e) {
            if (!cfg.get<bool>("fullscreen")) return;
            handleFullscreen();
        }

        void toggleFullscreenExecuted(object sender, ExecutedRoutedEventArgs e) {
            e.Handled = true;
            if (!app.isCartLoaded()) return;
            handleFullscreen();
        }

        void handleFullscreen() {
            cfg.toggle("fullscreen");
            if (!cfg.get<bool>("fullscreen")) {
                WindowStyle = WindowStyle.SingleBorderWindow;
                WindowState = WindowState.Normal;
                _Menu.Visibility = Visibility.Visible;
                Topmost = false;
                Mouse.OverrideCursor = null;
                FromFullscreen = true;
            } else {
                WindowStyle = WindowStyle.None;
                //WindowState = WindowState.Maximized;
                _Menu.Visibility = Visibility.Hidden;
                _Grid.Height = SystemParameters.FullPrimaryScreenHeight;
                _Grid.Width = SystemParameters.FullPrimaryScreenWidth;
                Topmost = true;
                Mouse.OverrideCursor = Cursors.None;
                FromFullscreen = false;
            }
            app.updateVideo();
        }

        public void resizeWindow() {
            int width = 0, height = 0;
            resolution.getResoltion(ref width, ref height);

            double addHeight = _Menu.BorderThickness.Top;
            addHeight += _Menu.BorderThickness.Bottom;
            addHeight += _Menu.Height;

            double addWidth = _Menu.BorderThickness.Left + _Menu.BorderThickness.Right;

            _Grid.Height = addHeight + height;
            _Grid.Width = addWidth + width;

            ControlHostElement.Height = height;
            ControlHostElement.Width = width;
        }

        void _ActivatedMenu(object sender, EventArgs e) {
            if (!IsActive) return;
            unpauseEmulation();
        }

        void _DeactivatedMenu(object sender, EventArgs e) {
            pauseEmulation();
        }

        void _Activated(object sender, EventArgs e) {
            if (!cfg.get<bool>("fullscreen")) {
                app.updateVideo();
            }
            unpauseEmulation();
        }

        void _Deactivated(object sender, EventArgs e) {
            pauseEmulation();
        }

        void _FocusChanged(object sender, DependencyPropertyChangedEventArgs e) {
            if (cfg.get<bool>("fullscreen")) return;
            if (IsFocused) {
               // unpauseEmulation();
            } else {
               // pauseEmulation();
            }
        }

        public void _StateChanged(object sender, EventArgs e) {
            switch (this.WindowState) {
                case WindowState.Maximized:
                case WindowState.Normal:
                default:
                    unpauseEmulation();
                    break;
                case WindowState.Minimized:
                    pauseEmulation();
                    break;
            }
        }

        public enum DimType { WORK_AREA_WIDTH, WORK_AREA_HEIGHT, WINDOW_BORDER_WIDTH, WINDOW_CAPTION_AND_BORDER_HEIGT };

        public int getDim( DimType dimtype) {
            int length = 0;
            switch (dimtype) {
                case DimType.WORK_AREA_WIDTH:
                    length = (int)SystemParameters.WorkArea.Width;
                    break;
                case DimType.WORK_AREA_HEIGHT:
                    length = (int)SystemParameters.WorkArea.Height;
                    break;
                case DimType.WINDOW_BORDER_WIDTH:
                    length =    (int)SystemParameters.FixedFrameVerticalBorderWidth * 2
                               + (int)SystemParameters.BorderWidth * 2;
                    break;
                case DimType.WINDOW_CAPTION_AND_BORDER_HEIGT:
                    length = (int)SystemParameters.FixedFrameHorizontalBorderHeight * 2
                             + (int)SystemParameters.BorderWidth * 2
                             + (int)SystemParameters.CaptionHeight + (int)_Menu.Height;
                    break;
            }
            return length;
        }

        public void getResolution(ref int width, ref int height) {
            resolution.getResoltion(ref width, ref height);
        }

        void Window_SizeChanged(object sender, SizeChangedEventArgs e) {
            _Window_SizeChanged();
        }

        public void _Window_SizeChanged() {
            Window.Left = (SystemParameters.WorkArea.Width - Window.ActualWidth) / 2
                + SystemParameters.WorkArea.Left;
            Window.Top = (SystemParameters.WorkArea.Height - Window.ActualHeight) / 2
                + SystemParameters.WorkArea.Top;
        }

        void generateRecentList()
        {
            MenuItem[] recentOptions = new MenuItem[] { lock_recent, clear_recent };
            recent_roms.Items.Clear();
            MenuItem mi;
            Image icon;
            if (recent == null) {
                recent = new Recent(cfg.getRecentList());
            }
            string[] recentlist = recent.getList();
            cfg.setRecentList(recentlist);

            foreach (string recentStr in recentlist) {
                if (recentStr == "") continue;
                mi = new MenuItem();
                mi.Header = recentStr;
                mi.Style = FindResource("mitem") as Style;
                mi.Click += new RoutedEventHandler(loadRecentRom);
                icon = new Image();
                icon.Source = new BitmapImage(new Uri("images/arrow.png", UriKind.Relative));
                icon.Height = 16;
                mi.Icon = icon;
                recent_roms.Items.Add(mi);
            }

            recent_roms.Items.Add( new Separator() );

            foreach(MenuItem recentOption in recentOptions ) {
                recent_roms.Items.Add(recentOption);
            }
        }

        void lockRecent(object sender, RoutedEventArgs e)
        {
            cfg.toggle("lock");
            updateMenuItemCheck((MenuItem)sender, "lock");
        }

        void clearRecent(object sender, RoutedEventArgs e)
        {
            if (MessageBox.Show(lang.get("clear_recent"), lang.get("clear_recent"), MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No) return;
            recent.clear();
            generateRecentList();
        }

        void loadRomDialogSMS(object sender, RoutedEventArgs e) {
            loadRomDialog(Cart._System.SMS);
        }

        void loadRomDialogSNES(object sender, RoutedEventArgs e) {
            loadRomDialog(Cart._System.SNES);
        }

        void loadRomDialog(Cart._System unit)
        {
            OpenFileDialog dlg = new OpenFileDialog();       
            dlg.DefaultExt = ".zip";
            switch ( unit ) {
                case Cart._System.SMS:
                    dlg.InitialDirectory = cfg.get<string>("sms_path_rom");
                    dlg.Filter = 
                        "SMS (.sms .rar .zip .7z .gz .bz2 .arj .tar) | *.sms; *.rar; *.zip; *.7z; *.gz; *.bz2; *.arj; *.tar"
                        + "|all (*.*) | *.*";
                    break;
                case Cart._System.SNES:
                    dlg.InitialDirectory = cfg.get<string>("snes_path_rom");
                    dlg.Filter = 
                        "SNES (.smc .sfc .swc .fig .bs .st .rar .zip .7z .gz .bz2 .arj .tar) | *.smc; *.sfc; *.swc; *.fig; *.bs; *.st; *.rar; *.zip; *.7z; *.gz; *.bz2; *.arj; *.tar"
                        +"|all (*.*) | *.*";
                    break;
                case Cart._System.GENESIS:
                    dlg.Filter = 
                        "GENESIS (.bin .smd .md .rar .zip .7z .gz .bz2 .arj .tar) | *.bin; *.smd; *.md; *.rar; *.zip; *.7z; *.gz; *.bz2; *.arj; *.tar"
                        + "|all (*.*) | *.*";
                    break;
            }
            Nullable<bool> result = false;
            try {
                result = dlg.ShowDialog();
            } catch {
                dlg.InitialDirectory = App.APP_DIR;
                result = dlg.ShowDialog();
            }
            if (result == true) {
                loadRom(dlg.FileName, unit);
            }
        }

        void loadRecentRom(object sender, RoutedEventArgs e)
        {
            MenuItem mi = (MenuItem)sender;
            string ident = (string)mi.Header;
            Cart._System unit;

            string[] words = ident.Split('|');
            foreach (KeyValuePair<Cart._System, string> pre in App.systemIdent) {
                if (pre.Value == words[0].Trim()) {
                    unit = pre.Key;
                    loadRom(words[1].Trim(), unit);
                    break;
                }
            }
        }

        public void updateVisibillity(ItemVisibillity itemVisibillity) {
            switch (itemVisibillity) {
                case ItemVisibillity.LOAD:
                    unload.IsEnabled = true;
                    power.IsEnabled = true;
                    reset.IsEnabled = true;
                    region_auto.IsEnabled = false;
                    region_us.IsEnabled = false;
                    region_jp.IsEnabled = false;
                    region_eu.IsEnabled = false;
                    set_fullscreen.IsEnabled = true;
                    break;
                case ItemVisibillity.UNLOAD:
                    unload.IsEnabled = false;
                    power.IsEnabled = false;
                    reset.IsEnabled = false;
                    region_auto.IsEnabled = true;
                    region_us.IsEnabled = true;
                    region_jp.IsEnabled = true;
                    region_eu.IsEnabled = true;
                    set_fullscreen.IsEnabled = false;
                    break;
            }
        }

        public void loadRom(string fn, Cart._System unit)
        {
            app.loadRom(fn, unit);
        }

        public void setTitle(string title)
        {
            Window.Title = title;
        }

        public void addRecent(string fn)
        {
            if (cfg.get<bool>("lock") == true) return;

            if (recent.add(fn)) {
                generateRecentList();
            }
        }

        void updateMenuItemCheck(MenuItem item, string idx)
        {
            Image icon = new Image();
            icon.Source = new BitmapImage(new Uri(cfg.get<bool>(idx) == false ? "images/uncheck.png" : "images/check.png", UriKind.Relative));
            icon.Height = 15;
            item.Icon = icon;
        }

        void setConfig()
        {
            updateMenuItemCheck(lock_recent, "lock");
            updateMenuItemCheck(vsync, "video_vsync");
            updateMenuItemCheck(show_fps, "show_fps");
            updateMenuItemCheck(audio_active, "audio_active");
            updateFullscreenResolution();
            updateWindowsResolution();
            updateHWFilter();
            updateSWFilter();
            updateRegion();
            updateSpeed();
        }

        private void closeWindow(object sender, EventArgs e)
        {
            if (settingswindow != null) {
                settingswindow.Close();
            }
            if (loaderwindow != null) {
                loaderwindow.Close();
            }
            app.kill();
        }

        void exitApp(object sender, EventArgs e)
        {
            this.Close();
        }

        void unloadCart(object sender, RoutedEventArgs e)
        {
            setTitle(App.APP_NAME);
            updateVisibillity(ItemVisibillity.UNLOAD);
            app.raiseEvent(Events.Job.UNLOAD);
        }

        void powerSystem(object sender, RoutedEventArgs e) {
            app.raiseEvent(Events.Job.POWER);
        }

        void resetSystem(object sender, RoutedEventArgs e) {
            app.raiseEvent(Events.Job.RESET);
        }

        public void openLoaderGui(string fn, LoaderWindow.Type type)
        {
            if (loaderwindow == null) {
                loaderwindow = new LoaderWindow(this, app, lang, cfg);
            }
            loaderwindow.prepare(fn, type);
            loaderwindow.Show();
        }

        public void closeLoaderGui()
        {
            if (loaderwindow != null) {
                loaderwindow.Hide();
                loaderwindow = null;
            }
        }

    }
}
