/***************************************************************************
         Copyright (c) Microsoft Corporation, All rights reserved.             
    This code sample is provided "AS IS" without warranty of any kind, 
    it is not recommended for use in a production environment.
***************************************************************************/

#include "project.h"
#include "coloritem.h"
#include <ctype.h>

#ifdef VS6
#define FF_STRIKETHROUGH  2
#endif

/*---------------------------------------------------------
  Prototypes
---------------------------------------------------------*/
void parseProperties( in ColorItem& colorItem, in COleStr style );

/*---------------------------------------------------------
  ColorItem
---------------------------------------------------------*/
ColorItem::ColorItem( in BSTR description, in BSTR style )
{
  TRACE_CREATE( "ColorItem", static_cast<IVsColorableItem*>(this) );

  m_refCount    = 1;
  m_description = bstrDup(description);
  m_foreColor   = CI_USERTEXT_FG;
  m_backColor   = CI_USERTEXT_BK;
  m_fontFlags   = 0;

  parseProperties( *this, style );

}


ColorItem::~ColorItem()
{
  TRACE_DESTROY( static_cast<IVsColorableItem*>(this) );
  bstrFree(m_description);
}

/*---------------------------------------------------------
  IUnknown
-----------------------------------------------------------*/
STDMETHODIMP ColorItem::QueryInterface( in REFIID iid, out void** obj )
{
  OUTARG(obj);

  if (iid == IID_IUnknown || iid == IID_IVsColorableItem)
  {
    TRACE("ColorItem::QueryInterface for IUnknown/IVsColorableItem");
    *obj = static_cast<IVsColorableItem*>(this);
  }
  else
    return E_NOINTERFACE;

  AddRef();
  return S_OK;
}

STDMETHODIMP_(ULONG) ColorItem::AddRef()
{
  return IncRefCount(&m_refCount);
}

STDMETHODIMP_(ULONG) ColorItem::Release()
{
  if (DecRefCount(&m_refCount) == 0)
  {
    delete this;
    return 0;
  }
  else
    return m_refCount;
}

/*---------------------------------------------------------
  IVsColorableItem
-----------------------------------------------------------*/
STDMETHODIMP ColorItem::GetDefaultColors (out COLORINDEX* foreColor, out COLORINDEX* backColor )
{
  if (foreColor) *foreColor = m_foreColor;
  if (backColor) *backColor = m_backColor;
  return S_OK;
}

STDMETHODIMP ColorItem::GetDefaultFontFlags ( out DWORD* fontFlags )
{
  if (fontFlags) *fontFlags = m_fontFlags;
  return S_OK;
}

STDMETHODIMP ColorItem::GetDisplayName ( out BSTR* description )
{
  if (description) *description = bstrDup(m_description);
  return S_OK;
}



/*---------------------------------------------------------
  Font weights & Text decoration
---------------------------------------------------------*/
struct FontFlagInfo 
{
  COleStr name;
  DWORD   flag;
};

static FontFlagInfo fontWeightTable[] =
{ 
  { OLE("normal"), 0 },
  { OLE("bold"),   FF_BOLD },
  { OLE("100"),    0 },
  { OLE("200"),    0 },
  { OLE("300"),    0 },
  { OLE("400"),    0 },
  { OLE("500"),    0 },  
  { OLE("600"),    FF_BOLD },
  { OLE("700"),    FF_BOLD },
  { OLE("800"),    FF_BOLD },
  { OLE("900"),    FF_BOLD },
  { OLE("bolder"), FF_BOLD },
  { OLE("lighter"),0 },
  { NULL,          0 }
};

static FontFlagInfo textDecorationTable[] =
{
  { OLE("line-through"),    FF_STRIKETHROUGH },
  { OLE("strike-through"),  FF_STRIKETHROUGH },
  { NULL, 0 }
};

/*---------------------------------------------------------
  Colors
---------------------------------------------------------*/
struct ColorInfo 
{
  COleStr    name;
  COLORINDEX color;
};

static ColorInfo fgColorTable[] = 
{
   { OLE("auto"),       CI_USERTEXT_FG      }
  ,{ OLE("text"),       CI_USERTEXT_FG      }
  ,{ OLE("selected"),   CI_SYSSEL_FG        }
  ,{ OLE("inactive"),   CI_SYSINACTSEL_FG   }
  ,{ OLE("plaintext"),  CI_SYSPLAINTEXT_FG  }    
  ,{ NULL,              CI_USERTEXT_FG      }
};

static ColorInfo bgColorTable[] =
{
   { OLE("auto"),       CI_USERTEXT_BK      }
  ,{ OLE("text"),       CI_USERTEXT_BK      }
  ,{ OLE("selected"),   CI_SYSSEL_BK        }
  ,{ OLE("inactive"),   CI_SYSINACTSEL_BK   }
  ,{ OLE("margin"),     CI_SYSWIDGETMGN_BK  }
  ,{ OLE("plaintext"),  CI_SYSPLAINTEXT_BK  }
  ,{ NULL,              CI_USERTEXT_BK      }
}; 

static ColorInfo colorTable[] =
{
   //VS predefined colors
   { OLE("black"),      CI_BLACK       }
  ,{ OLE("white"),      CI_WHITE       }
  ,{ OLE("maroon"),     CI_MAROON      }
  ,{ OLE("darkgreen"),  CI_DARKGREEN   }
  ,{ OLE("brown"),      CI_BROWN       }
  ,{ OLE("darkblue"),   CI_DARKBLUE    }
  ,{ OLE("purple"),     CI_PURPLE      }
  ,{ OLE("aquamarine"), CI_AQUAMARINE  }
  ,{ OLE("lightgray"),  CI_LIGHTGRAY   }
  ,{ OLE("darkgray"),   CI_DARKGRAY    }
  ,{ OLE("red"),        CI_RED         }
  ,{ OLE("green"),      CI_GREEN       }
  ,{ OLE("yellow"),     CI_YELLOW      }
  ,{ OLE("blue"),       CI_BLUE        }
  ,{ OLE("magenta"),    CI_MAGENTA     }
  ,{ OLE("cyan"),       CI_CYAN        }

  //Extra CSS defined colors in the VGA set
  ,{ OLE("lime"),   CI_GREEN      }
  ,{ OLE("aqua"),   CI_AQUAMARINE }
  ,{ OLE("fuchsia"),CI_MAGENTA    }
  ,{ OLE("gray"),   CI_DARKGRAY   }
  ,{ OLE("silver"), CI_LIGHTGRAY  }
  ,{ OLE("olive"),  CI_BROWN      }
  ,{ OLE("teal"),   CI_BLUE       }
  ,{ OLE("navy"),   CI_DARKBLUE   }
    
  ,{ NULL,          CI_USERTEXT_FG }
};


/*---------------------------------------------------------
  Text kinds
---------------------------------------------------------*/
struct TextKindInfo
{
  COleStr   name;
  ULONG     index;
};

static TextKindInfo textKindTable[] =
{
  { OLE("humantext"),   HUMAN_TEXT_ATTR },
  { OLE("programtext"), 0               },
  { NULL,               0               }
};


/*---------------------------------------------------------
  Table lookups
---------------------------------------------------------*/
COLORINDEX lookupColor( COleStr token, bool isForeGround )
{
    ColorInfo* info;

    //first try general color table
    for (info = colorTable; info->name; info++)
        if (oleICompare( info->name, token ) == 0) return info->color;

    //then try specific background/foreground table
    if (isForeGround) info = fgColorTable;
                 else info = bgColorTable;

    for (; info->name; info++)
        if (oleICompare( info->name, token ) == 0) return info->color;

    return info->color;         
}

DWORD lookupFontFlag( FontFlagInfo* table, COleStr token )
{
    FontFlagInfo* info;

    for( info = table; info->name; info++)
        if (oleICompare( info->name, token ) == 0)  return info->flag;
    
    return info->flag;
}


ULONG lookupTextKind( COleStr value )
{
    TextKindInfo* info;

    for( info = textKindTable; info->name; info++)
      if (oleICompare( info->name, value ) == 0) return info->index;

    return info->index;
}

/*---------------------------------------------------------
  property setters
---------------------------------------------------------*/
void setColor( ColorItem& colorItem, COleStr value, bool isForeGround )
{
  if (isForeGround) colorItem.m_foreColor = lookupColor(value,isForeGround);
              else  colorItem.m_backColor = lookupColor(value,isForeGround);
}

void setFontWeight( ColorItem& colorItem, COleStr value )
{
  colorItem.m_fontFlags |= lookupFontFlag( fontWeightTable, value);
}

void setTextDecoration( ColorItem& colorItem, COleStr value )
{
  colorItem.m_fontFlags |= lookupFontFlag( textDecorationTable, value );
}

void setTextKind( ColorItem& colorItem, COleStr value )
{
  //actually, the colorizer should adjust the colorclass 
  //with the HUMANTEXT flag if necessary; this implies building
  //a translation table just for the colorizer...yuck!

  //for now, we simply ignore this flag
}


//short hands
void setFgColor( ColorItem& colorItem, COleStr value )
{
  setColor( colorItem, value, true );
}

void setBgColor( ColorItem& colorItem, COleStr value )
{
  setColor( colorItem, value, false );
}

void setBold( ColorItem& colorItem, COleStr value )
{
  setFontWeight( colorItem, OLE("bold") );
}

void setLineThrough( ColorItem& colorItem, COleStr value )
{
  setTextDecoration( colorItem, OLE("line-through") );
}

void setHumanText( ColorItem& colorItem, COleStr value )
{
  setTextKind( colorItem, OLE("humantext") );
}

/*---------------------------------------------------------
  Get the property setter
---------------------------------------------------------*/
struct PropertyInfo
{
  COleStr name;
  void (*setter)( ColorItem& colorItem, COleStr value );
};

static PropertyInfo propertyTable[] =
{
  //official
  { OLE("color"),             setFgColor    },
  { OLE("background-color"),  setBgColor    },
  { OLE("font-weight"),       setFontWeight },
  { OLE("text-decoration"),   setTextDecoration },
  { OLE("text-kind"),         setTextKind   },

  //shorthands
  { OLE("bgcolor"),           setBgColor    },
  { OLE("bold"),              setBold       },
  { OLE("strike-through"),    setLineThrough},
  { OLE("line-through"),      setLineThrough},
  { OLE("humantext"),         setHumanText  },
  { NULL, NULL }
};

void setProperty( ColorItem& colorItem, COleStr name, COleStr value )
{
  PropertyInfo* info;
  for (info = propertyTable; info->name; info++)
  {
    if (oleICompare(name,info->name) == 0) 
    {
      info->setter( colorItem, value );
      return;
    }
  }
  return;
}

/*---------------------------------------------------------
  Parse a style specification
---------------------------------------------------------*/
COleStr skipWhiteSpace( COleStr str )
{
  while (oleIsSpace(*str) || *str == OLE('{') || *str == OLE('}'))
  {
    str++;
  }
  return str;
}


COleStr parseProperty( ColorItem& colorItem, COleStr str )
{
  const int MaxName = 255;

  OleChar name[MaxName+1];
  OleChar value[MaxName+1];
  int i;

  //scan the name
  i = 0;
  while (oleIsAlpha(*str) || *str == OLE('-'))
  {
    if (i < MaxName) name[i] = *str;
    i++;
    str++;
  }
  if (i < MaxName) name[i] = 0;
              else name[MaxName] = 0;
  str = skipWhiteSpace(str);
  
  //try to parse the value
  if (*str != OLE(':'))
  {
    oleCopy( value, OLE("default"), MaxName );
  }
  else
  {
    str++; //skip ':'
    str = skipWhiteSpace(str);
    
    //scan the value
    i = 0;
    while (oleIsAlphaNum(*str) || *str == OLE('#'))
    {
      if (i < MaxName) value[i] = *str;
      i++;
      str++;
    }
    if (i < MaxName) value[i] = 0;
                else value[MaxName] = 0;
    str = skipWhiteSpace(str);
  }

  //set the property
  setProperty( colorItem, name, value );
  return str;
}

void parseProperties( ColorItem& colorItem, COleStr str )
{
  if (str == NULL) return;

  //skip leading whitespace
  str = skipWhiteSpace( str );

  while (*str != 0 && *str != OLE('}'))
  {
    if (*str == OLE(';'))
    {
      //skip the semicolon
      str++;
    }
    else if (oleIsAlpha(*str)) 
    {
      //parse and set the property
      str = parseProperty(colorItem,str);
    }
    else
    {
      //something bad is happening, skip till next ';' or '}' or '\0'
      while (*str != OLE(';') && *str != OLE('}') && *str != 0)
      {
        str++;
      }
    }
    
    //always skip whitespace after a token
    str = skipWhiteSpace(str);
  }

  return;
}
                
