using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Drawing;
using System.IO;

// License: http://creativecommons.org/licenses/by-sa/2.0/uk/

namespace HewlettPackard
{
    /// <summary>
    /// Managed wrapper for the unmanaged LSPrintLauncher.dll
    /// </summary>
    public class LightScribe
    {
        public enum ContrastLevel
        {
            Best,
            Normal,
            Draft
        }

        private static IntPtr m_LightScribeDll = IntPtr.Zero;

        /// <summary>
        /// Pointer to the LSPrintLauncher.dll. Only created once.
        /// </summary>
        private static IntPtr LightScribeDll
        {
            get
            {
                if (m_LightScribeDll == IntPtr.Zero)
                {
                    string dllPath = GetDllPath();
                    if (dllPath == string.Empty) return IntPtr.Zero;
                    try
                    {
                        m_LightScribeDll = Kernel32.LoadWin32Library(dllPath);
                    }
                    catch (ApplicationException)
                    {
                        return IntPtr.Zero;
                    }
                }
                return m_LightScribeDll;
            }
        }

        /// <summary>
        /// Determines whether or not a LightScribe drive exists.
        /// </summary>
        public static bool HasLightScribeDrive
        {
            get
            {
                IntPtr pProc = Kernel32.GetProcAddress(LightScribeDll, "haveLSDrive");
                haveLSDrive cpv = (haveLSDrive)Marshal.GetDelegateForFunctionPointer(pProc, typeof(haveLSDrive));
                return cpv();
            }
        }

        /// <summary>
        /// Determines the path to LSPrintLauncher.dll, as described in
        /// the official SDK.
        /// </summary>
        /// <returns>empty string if path not found, otherwise absolute path</returns>
        private static string GetDllPath()
        {
            RegistryKey key = Registry.LocalMachine;
            key = key.OpenSubKey("Software\\LightScribe");
            if (key == null) return string.Empty;

            return key.GetValue("LsPrintLauncher", string.Empty) as string;
        }

        /// <summary>
        /// Generates an image which can be printed to disc (BMP).
        /// </summary>
        /// <param name="title">Title to show on disc</param>
        /// <param name="imagePath">Image to use as background</param>
        /// <param name="discNumber">Number of the disc. Values less than 1 will be ignored.</param>
        /// <returns>Path of the generated image (located in the TEMP-directory)</returns>
        public static string GenerateDiscImage(string title, string imagePath, int discNumber)
        {
            Image bitmap = new Bitmap(640, 640);

            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.Clear(Color.White);

                // Draw image to background
                if (!string.IsNullOrEmpty(imagePath) && File.Exists(imagePath))
                {
                    Image background = Bitmap.FromFile(imagePath);
                    g.DrawImage(background, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
                    background.Dispose();
                }

                if (discNumber > 0)
                {
                    title += string.Format(" (#{0})", discNumber);
                }

                if (!string.IsNullOrEmpty(title))
                {
                    Font textFont = new Font("Verdana", 25, FontStyle.Bold);
                    // Draw the text centered
                    SizeF textSize = g.MeasureString(title, textFont);
                    float yPos = (bitmap.Height * 0.75f);
                    float xPos = ((bitmap.Width - textSize.Width) / 2f);
                    // Make sure that font is visible, by drawing a background
                    g.FillRectangle(Brushes.White, xPos, yPos, textSize.Width, textSize.Height);
                    using (Pen p = new Pen(Color.Black, 4))
                    {
                        g.DrawRectangle(p, xPos, yPos, textSize.Width, textSize.Height);
                    }
                    g.DrawString(title, textFont, Brushes.Black, new PointF(xPos, yPos));
                }
            }
            // Determine save path
            string path = Path.GetTempFileName() + ".bmp";
            bitmap.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
            return path;
        }

        private delegate int launchPrintOptions(IntPtr args);
        private delegate int launchPrint(IntPtr args);
        private delegate bool haveLSDrive();

        /// <summary>
        /// Shows the LightScribe print options dialog.
        /// </summary>
        /// <param name="fileName">Path to the image to print.</param>
        /// <param name="deleteFile">Set to true, if the given image should automatically be deleted.</param>
        /// <returns>0 on success</returns>
        public static int ShowPrintOptions(string fileName, bool deleteFile)
        {
            IntPtr pProc = Kernel32.GetProcAddress(LightScribeDll, "launchPrintOptions");
            launchPrintOptions cpv = (launchPrintOptions)Marshal.GetDelegateForFunctionPointer(pProc, typeof(launchPrintOptions));

            string arguments = string.Format("--filename \"{0}\"", fileName);
            if (deleteFile)
            {
                arguments += " --deleteImageFile 1";
            }

            // We need to pass an *unmanaged* string (wchar_t*)
            IntPtr args = Marshal.StringToHGlobalUni(arguments);
            try
            {
                return cpv(args);
            }
            finally
            {
                Marshal.FreeHGlobal(args);
            }
        }

        /// <summary>
        /// Starts the printing process and shows its status.
        /// </summary>
        /// <param name="fileName">Path to the image file</param>
        /// <param name="index">Index of the target drive. The index only covers LS-enabled drives.</param>
        /// <param name="driveDisplayName">Display name of the drive.</param>
        /// <param name="driveLetter">Must match the index-parameter.</param>
        /// <param name="quality">Printing quality</param>
        /// <param name="deleteFile">Delete the file after finishing?</param>
        /// <example>
        /// From the official docs: The drive path must match 
        /// the actual path of the selected logical LightScribe drive. i.e. if there are 3 LightScribe 
        /// enabled drives, ‘E’, ‘F’ & ‘K’, they are normally drive indexes 0, 1 & 2 respectively.</example>
        /// <returns></returns>
        public static int ShowPrintProgress(string fileName, int index, string driveDisplayName, string driveLetter, ContrastLevel quality, bool deleteFile)
        {
            IntPtr pProc = Kernel32.GetProcAddress(LightScribeDll, "launchPrint");
            launchPrint cpv = (launchPrint)Marshal.GetDelegateForFunctionPointer(pProc, typeof(launchPrint));

            Dictionary<string, object> arguments = new Dictionary<string, object>();
            arguments["filename"] = fileName;
            arguments["index"] = index;
            arguments["name"] = driveDisplayName;
            arguments["path"] = driveLetter;
            arguments["quality"] = quality.ToString().ToLower();
            if (deleteFile)
            {
                arguments["deleteImageFile"] = "1";
            }

            string argumentString = "";
            foreach (KeyValuePair<string, object> argument in arguments)
            {
                argumentString += " --" + argument.Key + " \"" + argument.Value.ToString() + "\"";
            }

            // We need to pass an *unmanaged* string (wchar_t*)
            IntPtr args = Marshal.StringToHGlobalUni(argumentString);
            try
            {
                return cpv(args);
            }
            finally
            {
                Marshal.FreeHGlobal(args);
            }
        }

        /// <summary>
        /// Determines whether or not LightScribe is supported, that is
        /// if the DLL can be loaded and a LS drive exists.
        /// </summary>
        public static bool IsSupported
        {
            get
            {
                return (LightScribeDll != IntPtr.Zero && HasLightScribeDrive);
            }
        }
    }
}

