using System;
using System.Windows.Forms;
using System.IO;
 
namespace Raw2Picture
{
    public partial class Form1 : Form
    {
        private ColorFormatString cfs = null;
        private PreFilterString pfs = null;
        private PictureBuffer pictureBuffer = null;
        private string fileName = null;
        public static string NEWLINE = Environment.NewLine;
        private bool allowUserInput = false;
 
        public Form1()
        {
            InitializeComponent();
            cfs = new ColorFormatString();
            pfs = new PreFilterString();
        }
 
        private void Form1_Shown(object sender, EventArgs e)
        {
            textBox2.Text = "320";
            textBox3.Text = "240";
            textBox4.Text = "X32";
            textBox1.Text = "R8G8B8A8";
            pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
            setToolTips();
 
            log("Welcome to Raw2Picture by Sebastion Roll - Version 2008/10/06");
 
            try
            {
                String[] args = Environment.GetCommandLineArgs();
                if (args.Length >= 6)
                {
                    fileName = args[1];
                    log("input file set to: " + fileName);
                    textBox2.Text = args[2];    // width
                    textBox3.Text = args[3];    // height
                    textBox3.Text = args[4];    // pre filter
                    textBox1.Text = args[5];    // color format
                    convert();                     
                }
                if (args.Length >= 7)
                {
                    saveConvertedPicture(args[6]);
                }
                if (args.Length >= 8)
                {
                    if (args[7].Equals("noDialog"))
                    {
                        Application.Exit();
                    }
                }
            }
            catch (Exception ex)
            {
                handleError(ex.Message);
            }
 
            allowUserInput = true;
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                openFileDialog1.InitialDirectory = Application.StartupPath;
                openFileDialog1.Filter = "All Files (*.*)|*.*";
                openFileDialog1.Title = "Open File";
                DialogResult dr = openFileDialog1.ShowDialog();
                if (dr == DialogResult.OK)
                {
                    fileName = openFileDialog1.FileName;
 
                    if (fileName.ToLower().EndsWith("png") ||
                        fileName.ToLower().EndsWith("bmp") ||
                        fileName.ToLower().EndsWith("jpg") ||
                        fileName.ToLower().EndsWith("gif"))
                    {
                        loadPictureFromFile(fileName);
                    }
                    else
                    {
                        log("input file set to: " + fileName);
                        convert();
                    }
                }
            }
            catch (Exception ex)
            {
                handleError(ex.Message);
            }
        }
 
        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                if (pictureBuffer != null)
                {
                    saveFileDialog1.Filter = "All Files (*.*)|*.*";
                    saveFileDialog1.DefaultExt = "png";
                    saveFileDialog1.Title = "Save BMP or PNG or JPG or GIF";
                    DialogResult dr = saveFileDialog1.ShowDialog();
                    if (dr == DialogResult.OK)
                    {
                        saveConvertedPicture(saveFileDialog1.FileName);
                    }
                }
                else
                {
                    log("savePicture: nothing to save");
                }
            }
            catch (Exception ex)
            {
                handleError(ex.Message);
            }
        }
 
        private void convert()
        {
            FileStream fs = null;
            BinaryReader br = null;
 
            try
            {
                if (fileName == null)
                {
                    log("convert: no input file");
                    return;
                }
 
                if (!File.Exists(fileName))
                {
                    handleError("convert: input file not found: " + fileName);
                    return;
                }
 
                if (!isNumeric(textBox2.Text))
                {
                    log("convert: width is no numeric expression: " + textBox2.Text);
                    return;
                }
                if (Convert.ToInt32(textBox2.Text) == 0)
                {
                    log("convert: width is zero");
                    return;
                }
                if (!isNumeric(textBox3.Text))
                {
                    log("convert: height is no numeric expression: " + textBox3.Text);
                    return;
                }
                if (Convert.ToInt32(textBox3.Text) == 0)
                {
                    log("convert: height is zero");
                    return;
                }
 
                fs = new FileStream(fileName, FileMode.Open);
                br = new BinaryReader(fs);
 
                if (fs.Length == 0)
                {
                    log("convert: input file length is zero");
                    return;
                }
 
                String result = "";
 
                result = pfs.parse(textBox4.Text);
                if (result.Equals("ok"))
                {
                    log("PreFilter BytePerPixel: " + pfs.PerPixelByteCount);
                    log(String.Format("PreFilter DontCareBitMask : {0:X8}", pfs.DontCareBitMask));
                    log(String.Format("PreFilter SetToOneBitMask : {0:X8}", pfs.SetOneBitMask));
                    log(String.Format("PreFilter SetToZeroBitMask: {0:X8}", pfs.SetZeroBitMask));
 
                    result = cfs.parse(textBox1.Text);
                }
                if (result.Equals("ok"))
                {
                    log("ColorFormat BytePerPixel: " + cfs.PerPixelByteCount);
                    log("ColorFormat " + cfs.Red.ToString());
                    log("ColorFormat " + cfs.Green.ToString());
                    log("ColorFormat " + cfs.Blue.ToString());
                    log("ColorFormat " + cfs.Alpha.ToString());
 
                    if (pfs.PerPixelByteCount != cfs.PerPixelByteCount)
                    {
                        log("*** Warning: PreFilter.BytePerPixel != ColorFormatString.BytePerPixel");
                    }
 
                    int w = Convert.ToInt32(textBox2.Text);
                    int h = Convert.ToInt32(textBox3.Text);
                    uint color = 0;
                    uint rawData = 0;
                    uint data = 0;
 
                    if (pictureBuffer != null)
                    {
                        pictureBuffer.Dispose();
                    }
 
                    pictureBuffer = new PictureBuffer(w, h);
                    pictureBox1.Image = pictureBuffer.Bitmap;
 
                    long inputFileLength = fs.Length;
                    log("input file length (physical): " + inputFileLength + " byte");
                    long expectedFileLength = w * h * cfs.PerPixelByteCount;
                    log("input file length (width * height * BytePerPixel): " + expectedFileLength + " byte");
                    if (inputFileLength != expectedFileLength)
                    {
                        log("*** Warning: input file length != width * height * BytePerPixel");
                    }
 
                    for (int y = 0; y < h; y++)
                    {
                        for (int x = 0; x < w; x++)
                        {
                            if (cfs.PerPixelByteCount == 1)
                            {
                                rawData = (uint)br.ReadByte() << 24;
                            }
                            else if (cfs.PerPixelByteCount == 2)
                            {
                                rawData = (uint)br.ReadByte() << 24;
                                rawData |= (uint)br.ReadByte() << 16;
                            }
                            else if (cfs.PerPixelByteCount == 3)
                            {
                                rawData = (uint)br.ReadByte() << 24;
                                rawData |= (uint)br.ReadByte() << 16;
                                rawData |= (uint)br.ReadByte() << 8;
                            }
                            else if (cfs.PerPixelByteCount == 4)
                            {
                                rawData = (uint)br.ReadByte() << 24;
                                rawData |= (uint)br.ReadByte() << 16;
                                rawData |= (uint)br.ReadByte() << 8;
                                rawData |= (uint)br.ReadByte() << 0;
                            }
 
                            data = rawData;
 
                            uint setZero = pfs.SetZeroBitMask;
                            setZero = ~setZero;
                            data = data & setZero;
 
                            uint setOne = pfs.SetOneBitMask;
                            data |= setOne;
 
                            uint alpha = data & cfs.Alpha.BitMask;
                            alpha >>= 32 - (cfs.Alpha.BitStartPos + cfs.Alpha.BitCount);
                            alpha = (uint)Math.Round(alpha * 256.0 / Math.Pow(2, cfs.Alpha.BitCount));
                            if (cfs.Alpha.BitCount == 0)
                            {
                                alpha = 0xFF;
                            }
                            color = alpha << 24;
 
                            uint red = data & cfs.Red.BitMask;
                            red >>= 32 - (cfs.Red.BitStartPos + cfs.Red.BitCount);
                            red = (uint)Math.Round(red * 256.0 / Math.Pow(2, cfs.Red.BitCount));
                            color |= red << 16;
 
                            uint green = data & cfs.Green.BitMask;
                            green >>= 32 - (cfs.Green.BitStartPos + cfs.Green.BitCount);
                            green = (uint)Math.Round(green * 256.0 / Math.Pow(2, cfs.Green.BitCount));
                            color |= green << 8;
 
                            uint blue = data & cfs.Blue.BitMask;
                            blue >>= 32 - (cfs.Blue.BitStartPos + cfs.Blue.BitCount);
                            blue = (uint)Math.Round(blue * 256.0 / Math.Pow(2, cfs.Blue.BitCount));
                            color |= blue << 0;
 
                            // 4 bytes per pixel, format: ARGB
                            pictureBuffer.Integers[y * w + x] = color;
                        }
                    }
 
                    textBox5.Text = String.Format("{0:X" + cfs.PerPixelByteCount * 2 + "}", rawData >> (32 - cfs.PerPixelByteCount * 8));
                    textBox6.Text = String.Format("{0:X8}", color);
 
                    pictureBox1.Image = pictureBuffer.Bitmap;
 
                    log("convert: OK");
                }
                else
                {
                    log(result);
                }
            }
            catch (EndOfStreamException eofEx)
            {
                string eofStr = eofEx.Message + NEWLINE + "*** width or height too large ***";
                log(eofStr);
            }
            catch (Exception ex)
            {
                handleError(ex.Message);
            }
            finally
            {
                if (br != null)
                {
                    br.Close();
                }
                if (fs != null)
                {
                    fs.Close();
                    fs.Dispose();
                }
            }
        }
 
        public void log(string s)
        {
            logWindow.Text += s + NEWLINE;
            logWindow.SelectionStart = logWindow.Text.Length;
            logWindow.ScrollToCaret();
        }
 
        public void logProgress(int progress)
        {
            logWindow.Text = logWindow.Text.Substring(0, logWindow.Text.Length - 3);
            logWindow.Text += String.Format("{0:d3}", progress);
            logWindow.SelectionStart = logWindow.Text.Length;
            logWindow.ScrollToCaret();
        }
 
        public void handleError(String error)
        {
            log(error);
            MessageBox.Show(error, "Raw2Picture");
        }
 
        public static bool isNumeric(string s)
        {
            int retNum;
            return Int32.TryParse(s, out retNum);
        }
 
        private void setToolTips()
        {
            toolTip1.SetToolTip(textBox2, "Width of picture data");
            toolTip1.SetToolTip(textBox3, "Height of picture data");
            toolTip1.SetToolTip(textBox1, "How to interprete picture data" + NEWLINE +
                "Example 1: R5G6B5 <- MSB 5 bit is red channel. Following 6 bit is green channel. Following 5 bit is blue channel" + NEWLINE +
                "Example 2: A1R5G5B5 <- MSB 1 bit is alpha channel. Following 5 bit is red channel. Following 5 bit is green channel. Following 5 bit is blue channel" + NEWLINE +
                "Example 3: a8r8b8g8 <- MSB 8 bit is alpha channel. Following 8 bit is red channel. Following 8 bit is blue channel. Following 8 bit is green channel");
            toolTip1.SetToolTip(textBox4, "Change data before conversion start" + NEWLINE +
                "Example 1: X5S1X10 <- MSB 5 bit dont't care. Following 1 bit set to one. Following 10 bit don't care." + NEWLINE +
                "Example 2: S1X15 <- MSB 1 bit set to one. Following 15 bit don't care." + NEWLINE +
                "Example 3: X24Z8 <- MSB 24 bit don't care. Following 8 bit set to zero.");
            toolTip1.SetToolTip(textBox5, "Last bytes (1..4) of raw input stream");
            toolTip1.SetToolTip(textBox6, "Conversion value of last read input stream bytes in ARGB 8888 format");
            toolTip1.SetToolTip(button1, "Show dialog to open input file and then convert data" + NEWLINE +
                "If selected file is of type PNG, BMP, JPG or GIF: only PreView window is updated.");
            toolTip1.SetToolTip(button3, "Show dialog to save PreView as BMP, PNG, JPG or GIF");
            toolTip1.SetToolTip(button2, "Save PreView as PNG without any dialog");
            toolTip1.SetToolTip(button4, "Save PreView as BMP without any dialog");
            toolTip1.SetToolTip(button5, "Save PreView as JPG without any dialog");
            toolTip1.SetToolTip(button6, "Save PreView as raw ARGB8888 without any dialog");
            toolTip1.SetToolTip(button8, "Save PreView as raw RGBA8888 without any dialog");
            toolTip1.SetToolTip(pictureBox1, "Show result of data conversion" + NEWLINE +
                "Automatic updated on change of Width, Height, PreFilter, ColorFormat or InputData.");
            toolTip1.SetToolTip(button7, "Show help");
        }
 
        private void loadPictureFromFile(string fileName)
        {
            System.Drawing.Bitmap b = null;
 
            try
            {
                allowUserInput = false;
 
                b = new System.Drawing.Bitmap(fileName);
                if (b == null)
                {
                    log("cannot load file");
                    return;
                }
 
                textBox2.Text = "" + b.Width;
                textBox3.Text = "" + b.Height;
                textBox1.Text = "A8R8G8B8";
 
                logWindow.Text = "";
                log("Loading file as PreView, not converting anything. You can save, though.");
 
                if (pictureBuffer != null)
                {
                    pictureBuffer.Dispose();
                }
                pictureBuffer = new PictureBuffer(b.Width, b.Height);
 
                log("load progress...:   ");
 
                for (int y = 0; y < b.Height; y++)
                {
                    for (int x = 0; x < b.Width; x++)
                    {
                        pictureBuffer.Integers[y * b.Width + x] = (uint)b.GetPixel(x, y).ToArgb();
                    }
                    logProgress((int)(y * 101.0 / b.Height));
                    Update();
                }
 
                pictureBox1.Image = pictureBuffer.Bitmap;
 
                log("");
                log("load file OK");
                Update();
            }
            catch (Exception ex)
            {
                handleError(ex.Message);
            }
            finally
            {
                if (b != null)
                {
                    b.Dispose();
                }
 
                allowUserInput = true;
            }
        }
 
        public void saveConvertedPicture(string saveFileName)
        {
            if (pictureBuffer != null && saveFileName != null)
            {
                if (!saveFileName.Equals(""))
                {
                    try
                    {
                        pictureBuffer.Bitmap.Save(saveFileName);
                        log("file saved to: " + saveFileName);
                    }
                    catch (Exception ex)
                    {
                        handleError(ex.Message);
                    }
                }
                else
                {
                    log("savePicture: nothing to save");
                }
            }
            else
            {
                log("savePicture: nothing to save");
            }
        }
 
        public void quickSaveAs(string fileExtension)
        {
            if (fileName != null && pictureBuffer != null && fileExtension != null)
            {
                if (fileName != "" && fileExtension != "")
                {
                    saveConvertedPicture(fileName + fileExtension);
                }
                else
                {
                    log("quickSave: nothing to save");
                }
            }
            else
            {
                log("quickSave: nothing to save");
            }
        }
 
        private void safeRawData(RawFileGenerator.ByteFormat bf)
        {
            if (fileName != null && pictureBuffer != null)
            {
                if (fileName != "")
                {
                    RawFileGenerator rfg = new RawFileGenerator();
                    string result = rfg.writeRawFile(fileName + ".bin", pictureBuffer.Integers, bf);
                    if (result.Equals("ok"))
                    {
                        log("file saved to: " + fileName + ".bin");
                    }
                    else
                    {
                        log(result);
                    }
                }
                else
                {
                    log("quickSave: nothing to save");
                }
            }
            else
            {
                log("quickSave: nothing to save");
            }
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            quickSaveAs(".png");
        }
 
        private void button4_Click(object sender, EventArgs e)
        {
            quickSaveAs(".bmp");
        }
 
        private void button5_Click(object sender, EventArgs e)
        {
            quickSaveAs(".jpg");
        }
 
        private void button6_Click(object sender, EventArgs e)
        {
            safeRawData(RawFileGenerator.ByteFormat.ARGB8888);
        }
 
        private void button8_Click(object sender, EventArgs e)
        {
            safeRawData(RawFileGenerator.ByteFormat.RGBA8888);
        }
 
        private void button7_Click(object sender, EventArgs e)
        {
            HelpForm hf = new HelpForm();
            hf.ShowDialog(this);
        }
 
        private void button9_Click(object sender, EventArgs e)
        {
            if (allowUserInput && fileName != null)
            {
                if (fileName != "")
                {
                    HexViewForm hvf = new HexViewForm(fileName);
                    hvf.ShowDialog(this);
                    hvf.cleanup();
                }
            }
            else
            {
                log("In Hex View: nothing to show");
            }
        }
 
        private void button10_Click(object sender, EventArgs e)
        {
            if (allowUserInput)
            {
                if (pictureBuffer != null)
                {
                    string tempFile = Environment.CurrentDirectory + "\\temp.tmp";
                    RawFileGenerator rfg = new RawFileGenerator();
                    string result = rfg.writeRawFile(tempFile, pictureBuffer.Integers, RawFileGenerator.ByteFormat.ARGB8888);
                    if (result.Equals("ok"))
                    {
                        HexViewForm hvf = new HexViewForm(tempFile);
                        hvf.ShowDialog(this);
                        hvf.cleanup();
                        File.Delete(tempFile);
                    }
                    else
                    {
                        log("Out Hex View: cannot show output data");
                    }
                }
                else
                {
                    log("Out Hex View: nothing to show");
                }
            }
        }
 
        private void textBox2_TextChanged(object sender, EventArgs e)
        {
            if (allowUserInput)
            {
                convert();
            }
        }
 
        private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (allowUserInput)
            {
                if (Char.IsDigit(e.KeyChar))
                {
                    // Digits are OK
                }
                else if (e.KeyChar == '\b')
                {
                    // Backspace key is OK
                }
                else
                {
                    // Swallow this invalid key
                    e.Handled = true;
                }
            }
        }
 
        private void textBox3_TextChanged(object sender, EventArgs e)
        {
            if (allowUserInput)
            {
                convert();
            }
        }
 
        private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (allowUserInput)
            {
                if (Char.IsDigit(e.KeyChar))
                {
                    // Digits are OK
                }
                else if (e.KeyChar == '\b')
                {
                    // Backspace key is OK
                }
                else
                {
                    // Swallow this invalid key
                    e.Handled = true;
                }
            }
        }
 
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            if (allowUserInput)
            {
                String result = cfs.parse(textBox1.Text);
                if (result.Equals("ok"))
                {
                    convert();
                }
                else
                {
                    log(result);
                }
            }
        }
 
        private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (allowUserInput)
            {
                if (cfs.isValidFormatChar(e.KeyChar))
                {
                    // Color format syntax is OK
                }
                else if (Char.IsDigit(e.KeyChar))
                {
                    // Digits are OK
                }
                else if (e.KeyChar == '\b')
                {
                    // Backspace key is OK
                }
                else if (e.KeyChar == 0x0003)
                {
                    // Strg + C is OK
                }
                else if (e.KeyChar == 0x0016)
                {
                    // Strg + V is OK
                }
                else
                {
                    // Swallow this invalid key
                    e.Handled = true;
                }
            }
        }
 
        private void textBox4_TextChanged(object sender, EventArgs e)
        {
            if (allowUserInput)
            {
                String result = pfs.parse(textBox4.Text);
                if (result.Equals("ok"))
                {
                    convert();
                }
                else
                {
                    log(result);
                }
            }
        }
 
        private void textBox4_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (allowUserInput)
            {
                if (pfs.isValidFormatChar(e.KeyChar))
                {
                    // PreFilter format syntax is OK
                }
                else if (Char.IsDigit(e.KeyChar))
                {
                    // Digits are OK
                }
                else if (e.KeyChar == '\b')
                {
                    // Backspace key is OK
                }
                else if (e.KeyChar == 0x03)
                {
                    // Strg + C is OK
                }
                else if (e.KeyChar == 0x16)
                {
                    // Strg + V is OK
                }
                else
                {
                    // Swallow this invalid key
                    e.Handled = true;
                }
            }
        }
 
        private void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 0x1b)
            {
                Application.Exit();
            }
        }
    }
}