using System;
 
namespace Raw2Picture
{
    public class ColorFormatString
    {
        public class ColorComponent
        {
            public uint BitMask;
            public int BitStartPos;
            public int BitCount;
            public string Name = "";
 
            public void reset(string myName)
            {
                BitMask = 0;
                BitStartPos = 0;
                BitCount = 0;
                Name = myName;
            }
 
            public override string ToString()
            {
                return Name +
                    String.Format("   BitMask:{0:X8}", BitMask) +
                    String.Format("   BitStartPos:{0:d2}", BitStartPos) +
                    String.Format("   BitCount:{0:d2}", BitCount);
            }
        }
 
        public ColorFormatString()
        {
            red = new ColorComponent();
            green = new ColorComponent();
            blue = new ColorComponent();
            alpha = new ColorComponent();
            reset();   
        }
 
        private void reset()
        {
            theString = "";
            perPixelBitCount = 0;
            perPixelByteCount = 0;
            red.reset("red  ");
            green.reset("green");
            blue.reset("blue ");
            alpha.reset("alpha");
        }
 
        private String theString;
        public String TheString { get { return theString; } }
 
        private int perPixelByteCount;
        public int PerPixelByteCount { get { return perPixelByteCount; } }
 
        private int perPixelBitCount;
        public int PerPixelBitCount { get { return perPixelBitCount; } }
 
        private ColorComponent red;
        public ColorComponent Red { get { return red; } }
 
        private ColorComponent green;
        public ColorComponent Green { get { return green; } }
 
        private ColorComponent blue;
        public ColorComponent Blue { get { return blue; } }
 
        private ColorComponent alpha;
        public ColorComponent Alpha { get { return alpha; } }
 
        public readonly char[] ValidFormatChars = { 'r', 'g', 'b', 'a', 'R', 'G', 'B', 'A' };
 
        public String parse(string s)
        {
            int bitMaskStart = 0;
 
            reset();
 
            if (s.Length == 0)
            {
                return "ColorFormatString: string length is zero";
            }
 
            theString = "" + s;
 
            for (int i = 0; i < s.Length; i += 2)
            {
                char currentChar = s[i];
 
                if (!isValidFormatChar(currentChar))
                {
                    reset();
                    String err = "ColorFormatString: unknown char: " + currentChar;
                    err += Form1.NEWLINE + "hint: following chars are OK: ";
                    for (int e = 0; e < ValidFormatChars.Length; e++)
                    {
                        err += ValidFormatChars[e] + " ";
                    }
                    return err;
                }
                if (s.Length <= i + 1)
                {
                    reset();
                    return "ColorFormatString: unexpected end of string" +
                        Form1.NEWLINE + "hint: each char must follow a number" +
                        Form1.NEWLINE + "hint: current char is: " + currentChar;
                }
                if (!Form1.isNumeric(s[i + 1].ToString()))
                {
                    reset();
                    return "ColorFormatString: each char must follow a number" +
                        Form1.NEWLINE + "hint: current char is: " + currentChar +
                        Form1.NEWLINE + "hint: this is not a number: " + s[i + 1];
                }
 
                try
                {
                    int bitCount = Convert.ToInt32(s[i + 1].ToString());
                    if (s.Length > i + 2)
                    {
                        if (Form1.isNumeric(s[i + 2].ToString()))
                        {
                            String twoCharNumber = "" + s[i + 1].ToString() + s[i + 2].ToString();
                            bitCount = Convert.ToInt32(twoCharNumber);
                            i++;
 
                            if (s.Length > i + 2)
                            {
                                if (Form1.isNumeric(s[i + 2].ToString()))
                                {
                                    reset();
                                    return "ColorFormatString: number too large, only 0..99 supported.";
                                }
                            }
                        }
                    }
 
                    uint mask = 0x00000000;
 
                    for (int k = 0; k < bitCount; k++)
                    {
                        int bitPos = bitMaskStart + k;
                        mask |= (uint)((0x80000000) >> bitPos);
                    }
 
                    if ((currentChar == 'r') || (currentChar == 'R'))
                    {
                        if (red.BitCount != 0 || red.BitMask != 0 || red.BitStartPos != 0)
                        {
                            reset();
                            return "ColorFormatString error: multiple occurrence of char: " + currentChar;
                        }
                        red.BitMask = mask;
                        red.BitStartPos = bitMaskStart;
                        red.BitCount = bitCount;
                    }
                    else if ((currentChar == 'g') || (currentChar == 'G'))
                    {
                        if (green.BitCount != 0 || green.BitMask != 0 || green.BitStartPos != 0)
                        {
                            reset();
                            return "ColorFormatString error: multiple occurrence of char: " + currentChar;
                        }
                        green.BitMask = mask;
                        green.BitStartPos = bitMaskStart;
                        green.BitCount = bitCount;
                    }
                    else if ((currentChar == 'b') || (currentChar == 'B'))
                    {
                        if (blue.BitCount != 0 || blue.BitMask != 0 || blue.BitStartPos != 0)
                        {
                            reset();
                            return "ColorFormatString error: multiple occurrence of char: " + currentChar;
                        }
                        blue.BitMask = mask;
                        blue.BitStartPos = bitMaskStart;
                        blue.BitCount = bitCount;
                    }
                    else if ((currentChar == 'a') || (currentChar == 'A'))
                    {
                        if (alpha.BitCount != 0 || alpha.BitMask != 0 || alpha.BitStartPos != 0)
                        {
                            reset();
                            return "ColorFormatString error: multiple occurrence of char: " + currentChar;
                        }
                        alpha.BitMask = mask;
                        alpha.BitStartPos = bitMaskStart;
                        alpha.BitCount = bitCount;
                    }
                    else
                    {
                        reset();
                        return "ColorFormatString error: unknown char: " + currentChar;
                    }
 
                    bitMaskStart += bitCount;
 
                }
                catch (Exception ex)
                {
                    reset();
                    return "ColorFormatString error: " + ex.Message;
                }
            }
 
            perPixelBitCount = bitMaskStart;
 
            if (perPixelBitCount == 8)
            {
                perPixelByteCount = 1;
            }
            else if (perPixelBitCount == 16)
            {
                perPixelByteCount = 2;
            }
            else if (perPixelBitCount == 24)
            {
                perPixelByteCount = 3;
            }
            else if (perPixelBitCount == 32)
            {
                perPixelByteCount = 4;
            }
            else if (perPixelBitCount > 32)
            {
                int bc = perPixelBitCount;
                reset();
                return "ColorFormatString: BitCount too large, only 32 allowed. BitCount = " + bc;
            }
            else 
            {
                int bc = perPixelBitCount;
                reset();
                return "ColorFormatString: BitCount not byte aligned. BitCount = " + bc;
            }    
 
            return "ok";
        }
 
        public bool isValidFormatChar(char c)
        {
            for (int i = 0; i < ValidFormatChars.Length; i++)
            {
                if (c == ValidFormatChars[i])
                {
                    return true;
                }
            }
 
            return false;
        }
    }
}