/*
 * Copyright 2020 faddenSoft
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using CommonWPF;
using Microsoft.Win32;

namespace SourceGen.WpfGui {
    /// <summary>
    /// Export an image from the visualization editor.
    /// </summary>
    public partial class ExportVisualization : Window, INotifyPropertyChanged {
        private Visualization mVis;
        private WireframeObject mWireObj;
        private string mFileNameBase;

        // INotifyPropertyChanged implementation
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public bool IsBitmap {
            get { return mIsBitmap; }
            set { mIsBitmap = value; OnPropertyChanged(); }
        }
        public bool IsWireframe {
            get { return !mIsBitmap; }
        }
        private bool mIsBitmap;

        /// <summary>
        /// Item for output size combo box.
        /// </summary>
        public class OutputSize {
            public int Width { get; private set; }
            public int Height { get; private set; }
            public override string ToString() {
                return Width + "x" + Height;
            }

            public OutputSize(int width, int height) {
                Width = width;
                Height = height;
            }
        }

        /// <summary>
        /// List of output sizes, for combo box.
        /// </summary>
        public List<OutputSize> OutputSizeList { get; private set; }


        public ExportVisualization(Window owner, Visualization vis, WireframeObject wireObj,
                string fileNameBase) {
            InitializeComponent();
            Owner = owner;
            DataContext = this;

            mVis = vis;
            mWireObj = wireObj;
            mFileNameBase = fileNameBase;

            OutputSizeList = new List<OutputSize>();

            // Normally, bitmap and wireframe visualizations don't really differ, because
            // we're just working off the cached image rendering.  It matters for us though,
            // so we need to see if a wireframe-only parameter exists.
            bool isWireframe = (vis is VisWireframeAnimation) ||
                vis.VisGenParams.ContainsKey(VisWireframeAnimation.P_IS_ANIMATED);
            IsBitmap = !isWireframe;

            if (isWireframe) {
                int dim = 64;
                while (dim <= 1024) {
                    OutputSizeList.Add(new OutputSize(dim, dim));
                    dim *= 2;
                }
            } else {
                int baseWidth = (int)vis.CachedImage.Width;
                int baseHeight = (int)vis.CachedImage.Height;
                // ensure there's at least one entry, then add other options
                OutputSizeList.Add(new OutputSize(baseWidth, baseHeight));
                int mult = 2;
                while (baseWidth * mult < 2048 && baseHeight * mult < 2048) {
                    OutputSizeList.Add(new OutputSize(baseWidth * mult, baseHeight * mult));
                    mult *= 2;
                }
            }

            sizeComboBox.SelectedIndex = 0;
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e) {
            SaveFileDialog fileDlg = new SaveFileDialog() {
                Filter = Res.Strings.FILE_FILTER_GIF + "|" + Res.Strings.FILE_FILTER_ALL,
                FilterIndex = 1,
                ValidateNames = true,
                AddExtension = true,
                FileName = mFileNameBase + ".gif"
            };
            if (fileDlg.ShowDialog() != true) {
                return;
            }
            string pathName = Path.GetFullPath(fileDlg.FileName);
            Debug.WriteLine("Save path: " + pathName);

            try {
                OutputSize item = (OutputSize)sizeComboBox.SelectedItem;
                if (mVis is VisWireframeAnimation) {
                    Debug.Assert(item.Width == item.Height);
                    AnimatedGifEncoder encoder = new AnimatedGifEncoder();
                    ((VisWireframeAnimation)mVis).EncodeGif(encoder, item.Width);

                    using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
                        encoder.Save(stream, out int dispWidth, out int dispHeight);
                    }
                } else {
                    BitmapSource outImage;
                    if (IsBitmap) {
                        int scale = item.Width / (int)mVis.CachedImage.Width;
                        Debug.Assert(scale >= 1);
                        if (scale == 1) {
                            outImage = mVis.CachedImage;
                        } else {
                            outImage = mVis.CachedImage.CreateScaledCopy(scale);
                        }
                    } else {
                        Debug.Assert(item.Width == item.Height);
                        outImage = Visualization.GenerateWireframeImage(mWireObj,
                            item.Width, mVis.VisGenParams);
                    }

                    GifBitmapEncoder encoder = new GifBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(outImage));

                    using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
                        encoder.Save(stream);
                    }
                }
            } catch (Exception ex) {
                // Error handling is a little sloppy, but this shouldn't fail often.
                MessageBox.Show(ex.Message, Res.Strings.ERR_FILE_GENERIC_CAPTION,
                    MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            // After successful save, close dialog box.
            DialogResult = true;
        }
    }
}
