{***********************************************************************************
*  Gen_sGraph - Solid Graph Plugin for Coolmon 2                                   *
*  Copyright (C) 2007 Alch                                                         *
*                                                                                  *
*  This library is free software; you can redistribute it and/or                   *
*  modify it under the terms of the GNU Lesser General Public                      *
*  License as published by the Free Software Foundation; either                    *
*  version 2.1 of the License, or (at your option) any later version.              *
*                                                                                  *
*  This library is distributed in the hope that it will be useful,                 *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of                  *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU               *
*  Lesser General Public License for more details.                                 *
*                                                                                  *
*  You should have received a copy of the GNU Lesser General Public                *
*  License along with this library; if not, write to the Free Software             *
*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  *
*                                                                                  *
***********************************************************************************}

unit PlugServ;
{$WARN SYMBOL_PLATFORM OFF}

interface

uses
	Windows, ActiveX, Classes, ComObj, Graphics, SysUtils,
		CM2VISInterface, CM2AutoUpdateInterface, CM2EditorSupportInterface;

type
  TGraph = Record
    Points: Array of TPoint;
    FillColor, EndColor, EdgeColor: TColor;
    Average, Value, Max: Single;
    Gradient: Boolean;
    bStyle: TBrushStyle;
  end;
	TCM2sGraph = class(TComObject, ICM2VisPlug, ICM2AutoUpdate, ICM2EditorSupportVis10)
	private
		fDC: HDC;  
		fBMP: TBitmap;
		LastErrorMsg: String;
    Graph: Array[1..8] of TGraph; //set equal to MaxCount
    Fill: Array[1..8] of TColor;  //
		Border, Edge, ShowValue, BarGraph, BarAvg, BottomToTop,
    TextRight, TextBottom, GridBack, GridFront, InputOrder, RevOrder: Boolean;
		fLeft, fTop, fWidth, fHeight, BarWidth, gMin, gLength, GridX, GridY, StartPoint: Integer;
    FontSize, Padding, GraphCount, History, EdgeWidth, BorderWidth, GradDirection: Byte;
		BorderColor, GridColor, BackgroundColor, FontColor: TColor;
		GridStyle, EdgeStyle: TPenStyle;
		FontName, ValueString: String;
		FontStyle: TFontStyles;
		procedure DrawGraph;
		procedure DrawGrid;
		procedure CalcValues;
    procedure pGraph(Index: Byte);
		procedure DrawBars;
		procedure GradientFill;
		procedure InitGP;
		function GetGradient(StartColor, EndColor: TColor; PointValue, Min, Max: Single): TColor;
	public
		Function Name(OUT Name: PChar): HResult; stdcall;
		Function Version(OUT Version: Word): HResult; stdcall;
		Function RequiredCoreVersion(OUT RCV: Word): HResult; stdcall;
		Function GetLastErrorMsg(OUT Msg: PChar): HResult; stdcall;
		Function GetInfoCount(OUT InfoCount: Integer): HResult; stdcall;
		Function GetInfo(Index: Integer; OUT InfoName: PChar; OUT InfoVar: PChar): HResult; stdcall;
		Function SetDC(CONST NewDC: THandle): HResult; stdcall;
		Function SetDimensions(Left, Top, Width, Height : Integer): HResult; stdcall;
		Function SetConfigString(NewConfigString: PChar): HResult; stdcall;
		Function Redraw: HResult; stdcall;
		Function Update(SensorStyle: Byte; NewString: PChar; NewValue: Double): HResult; stdcall;
		Function Init(PaintDC: THandle): HResult; stdcall;
		Function Terminating: HResult; stdcall;
		Function Setup: HResult; stdcall;
		Function GetDefaultCount(OUT DefaultCount: Integer): HResult; stdcall;
		Function GetDefault(Index: Integer; OUT Name: PChar; OUT Value: PChar; OUT Description: PChar): HResult; stdcall;
		Function GetOptionalCount(OUT OptionalCount: Integer): HResult; stdcall;
		Function GetOptional(Index: Integer; OUT Name: PChar; OUT Value: PChar; OUT Description: PChar): HResult; stdcall;
		Function UpdateFileURL(OUT URL: PChar): HResult; stdcall;
		Function ObjIns(ParameterName: PChar; OUT ValueType: Byte; OUT ValueRange: PChar): HResult; stdcall;
	end;

const
	Class_CM2sGraph: TGUID = '{5171ABAD-F4C1-4D58-B61A-D9B2D4570DB1}';
  MaxCount: Byte = 8; //Max Number of Graphs

implementation

uses
	ComServ, Filter, PluginTools;

{ TCM2sGraph }

function TCM2sGraph.Name(out Name: PChar): HResult;
begin
	Name := 'sGraph';
	Result := S_OK;
end;

function TCM2sGraph.Version(out Version: Word): HResult;
begin
	Version := 0260;
	Result := S_OK;
end;

function TCM2sGraph.UpdateFileURL(out URL: PChar): HResult;
begin
	URL := 'http://www.morbidmetal.org/cm2/Vis_sGraph.upd';
	Result := S_OK;
end;

function TCM2sGraph.RequiredCoreVersion(out RCV: Word): HResult;
begin
	RCV := 0500;
	Result := S_OK;
end;

function TCM2sGraph.GetInfo(Index: Integer; out InfoName, InfoVar: PChar): HResult;
begin
	Result := S_OK;
	Case Index of
		0: Begin
			InfoName := 'Programmed By';
			InfoVar := 'Alch';
		End;
		1: Begin
			InfoName := 'Created';
			InfoVar := 'October 5, 2006';
		End;
		2: Begin
			InfoName := 'Last Update';
			InfoVar := 'August 15, 2007';
		End;   
		3: Begin
			InfoName := 'Copyright';
			InfoVar := '(C) 2007 Alch';
		End;
		4: Begin
			InfoName := 'License';
			InfoVar := 'LGPL - GNU Lesser General Public License';
		End;
  else
    Result := E_FAIL;
    LastErrorMsg := 'Info Index Out of Bounds';
	end;
end;

function TCM2sGraph.GetInfoCount(out InfoCount: Integer): HResult;
begin
	InfoCount := 5;
	Result := S_OK;
end;

function TCM2sGraph.Redraw: HResult;
var
	Canvas: TCanvas;
begin
	Result := S_OK;

  Canvas := TCanvas.Create;
	try
		Canvas.Handle := fDC;
    if History = 0 then
      DrawGraph;
		Canvas.Draw(fLeft, fTop, fBMP);
	except
		LastErrorMsg := 'Redraw Error';
		Result := E_FAIL;
	end;
  Canvas.Free;
end;

Procedure TCM2sGraph.DrawGraph;
  procedure Layers;
  var
    Order: Array of Byte;
    NoChange: Boolean;
    i, tmp: Byte;
  begin
      SetLength(Order, GraphCount+1);
      for i := 1 to GraphCount do
        Order[i] := i;

      Repeat
        NoChange := True;
        for i := 1 to GraphCount-1 do
          if Graph[Order[i]].Average < Graph[Order[i+1]].Average then
          begin
            NoChange := False;
            tmp := Order[i];
            Order[i] := Order[i+1];
            Order[i+1] := tmp;
          end;
      Until NoChange;

      for i := 1 to GraphCount do
        pGraph(Order[i]);
  end;
  function PadVal(Str: String): String;
  var
    i: Integer;
    Pad: String;
  begin
    Pad := '';
    if Length(Str) < Padding then
      for i := 1 to Padding - Length(Str) do
        Pad := Pad + '0';

    Result := Pad + Str;
  end;
var
  l, t, i: Integer;
  Str: String;
begin
	fBMP.Width := fWidth;
	fBMP.Height := fHeight;

	With fBMP.Canvas do
	begin
		Brush.Style := bsSolid;
		Brush.Color := BackgroundColor;
		FillRect(Rect(0, 0, fWidth, fHeight));

		if GridBack then
			DrawGrid;
      
		if Not Edge then
			Pen.Style := psClear;

    if GraphCount = 1 then
      pGraph(1)
    else if InputOrder then
      for i := 1 to GraphCount do
        pGraph(i)
    else if RevOrder then
      for i := GraphCount downto 1 do
        pGraph(i)
    else if BarGraph and Not BarAvg then
      DrawBars
    else
      Layers;

		if GridFront then
			DrawGrid;

    for i := 1 to GraphCount do
      if Graph[i].Gradient then
      begin
        GradientFill;
        Break;
      end;

		if ShowValue then
		begin            
      Brush.Style := bsClear;
			Font.Name := FontName;
			Font.Size := FontSize;
			Font.Style := FontStyle;
			Font.Color := FontColor;

      for i := 1 to GraphCount do
      begin
        Str := StringReplace(ValueString, '%v', PadVal(FloatToStr(Graph[i].Value)), [rfIgnoreCase]);
        if TextRight then
          l := fWidth - TextWidth(Str) - 10
        else
          l := 10;
        if TextBottom then
          t := fHeight - 5 - (TextHeight(Str) * (GraphCount + 1 - i))
        else
          t := 5 + (TextHeight(Str) * (i - 1));

        TextOut(l, t, Str);
      end;
		end;

		if Border then
		begin     
      Brush.Style := bsClear;
			Pen.Color := BorderColor;
			Pen.Style := psInsideFrame;
			Pen.Width := BorderWidth;
			Rectangle(0, 0, fWidth, fHeight);
		end;
	end;
end;

Procedure TCM2sGraph.pGraph(Index: Byte);
var
  i: Integer;
begin
  with fBMP.Canvas do
  begin
    if Graph[Index].Gradient then
      Brush.Color := Fill[Index]
    else
      Brush.Color := Graph[Index].FillColor;
    Brush.Style := Graph[Index].bStyle;
    if Edge then
    begin
      Pen.Color := Graph[Index].EdgeColor;
      Pen.Width := EdgeWidth;
      if EdgeWidth = 1 then
        Pen.Style := EdgeStyle
      else
        Pen.Style := psInsideFrame;
      end;
    if BarGraph then
      for i := 2 to gLength + 1 do
        Rectangle(Graph[Index].Points[i].X - BarWidth, Graph[Index].Points[i].Y, Graph[Index].Points[i].X, StartPoint)
    else
      Polygon(Graph[Index].Points);
  end;
end;

Procedure TCM2sGraph.DrawBars;
var
	i, j, tmp: Integer;
  Order: Array of Byte;
  NoChange: Boolean;
begin
  SetLength(Order, GraphCount + 1);
  for i := 1 to GraphCount do
    Order[i] := i;

  if Edge then
    with fBMP.Canvas do
    begin
      Pen.Width := EdgeWidth;
      if EdgeWidth = 1 then
        Pen.Style := EdgeStyle
      else
        Pen.Style := psInsideFrame;
    end;

  for i := 2 to gLength + 1 do
  begin
    Repeat
      NoChange := True;
      if BottomToTop then
      begin
        for j := 1 to GraphCount -1 do
          if Graph[Order[j]].Points[i].Y > Graph[Order[j+1]].Points[i].Y then
          begin
            NoChange := False;
            tmp := Order[j];
            Order[j] := Order[j+1];
            Order[j+1] := tmp;
          end;
      end else begin
        for j := 1 to GraphCount -1 do
          if Graph[Order[j]].Points[i].Y < Graph[Order[j+1]].Points[i].Y then
          begin
            NoChange := False;
            tmp := Order[j];
            Order[j] := Order[j+1];
            Order[j+1] := tmp;
          end;
      end;
    Until NoChange;

    with fBMP.Canvas do
      for j := 1 to GraphCount do
      begin
        if Graph[Order[j]].Gradient then
          Brush.Color := Fill[Order[j]]
        else
          Brush.Color := Graph[Order[j]].FillColor;
        Brush.Style := Graph[Order[j]].bStyle;
        Pen.Color := Graph[Order[j]].EdgeColor;

        Rectangle(Graph[Order[j]].Points[i].X - BarWidth, Graph[Order[j]].Points[i].Y, Graph[Order[j]].Points[i].X, StartPoint);
      end;
  end;
end;  

Procedure TCM2sGraph.DrawGrid;
var
	i: Integer;
	space: Single;
begin
  if GridX + GridY < 1 then
    Exit;

	with fBMP.Canvas do
	begin
    Brush.Style := bsClear;
		Pen.Color := GridColor;
		Pen.Style := GridStyle;
		Pen.Width := 1;

		If GridX > 0 then
		begin
			space := fWidth / (GridX + 1);
			for i := 1 to GridX do
			begin
				MoveTo(Round(space * i), 0);
				LineTo(Round(space * i), fHeight - 1);
			end;
		end;

		if GridY > 0 then
		begin
			space := fHeight / (GridY + 1);
			for i := 1 to GridY do
			begin
				MoveTo(0, Round(space * i));
				LineTo(fWidth - 1, Round(space * i));
			end;
		end;
	end;
end;

Procedure TCM2sGraph.GradientFill;
  function ColorToRGBTriple(Clr: TColor): TRGBTriple;
  begin
    with Result do
    begin
      rgbtRed   := GetRValue(Clr);
      rgbtGreen := GetGValue(Clr);
      rgbtBlue  := GetBValue(Clr);
    end;
  end;
  function RGBTripleToColor(Clr: TRGBTriple): TColor;
  begin
    Result := RGB(Clr.rgbtRed, Clr.rgbtGreen, Clr.rgbtBlue);
  end;
type
	pRGBTripleArray = ^TRGBTripleArray;
	TRGBTripleArray = Array[Word] of TRGBTriple;
var
	x, y, i: Integer;
	Row: pRGBTripleArray;
	Grad: Array[1..8] of TRGBTriple; //MaxCount
	tmp: TColor;
begin
  Case GradDirection of
  1: {BottomToTop}
	  for y := 0 to fHeight-1 do
	  begin
		  Row := fBMP.ScanLine[y];
      for i := 1 to GraphCount do
        Grad[i] := ColorToRGBTriple(GetGradient(Graph[i].EndColor, Graph[i].FillColor, y, 0, fHeight-1));
		  for x := 0 to fWidth-1 do
      begin
        tmp := RGBTripleToColor(Row[x]);
        for i := 1 to GraphCount do
			    if Graph[i].Gradient and (tmp = Fill[i]) then
				    Row[x] := Grad[i];
      end;
	  end;
  2: {LeftToRight}
	  for x := 0 to fWidth-1 do
	  begin
      for i := 1 to GraphCount do
        Grad[i] := ColorToRGBTriple(GetGradient(Graph[i].FillColor, Graph[i].EndColor, x, 0, fWidth-1));
		  for y := 0 to fHeight-1 do
      begin
		    Row := fBMP.ScanLine[y];
        tmp := RGBTripleToColor(Row[x]);
        for i := 1 to GraphCount do
			    if Graph[i].Gradient and (tmp = Fill[i]) then
				    Row[x] := Grad[i];
      end;
	  end;
  3: {RightToLeft}
	  for x := 0 to fWidth-1 do
	  begin
      for i := 1 to GraphCount do
        Grad[i] := ColorToRGBTriple(GetGradient(Graph[i].EndColor, Graph[i].FillColor, x, 0, fWidth-1));
		  for y := 0 to fHeight-1 do
      begin
		    Row := fBMP.ScanLine[y];
        tmp := RGBTripleToColor(Row[x]);
        for i := 1 to GraphCount do
			    if Graph[i].Gradient and (tmp = Fill[i]) then
				    Row[x] := Grad[i];
      end;
	  end;
  else {TopToBottom}
	  for y := 0 to fHeight-1 do
	  begin
		  Row := fBMP.ScanLine[y];
      for i := 1 to GraphCount do
        Grad[i] := ColorToRGBTriple(GetGradient(Graph[i].FillColor, Graph[i].EndColor, y, 0, fHeight-1));
		  for x := 0 to fWidth-1 do
      begin
        tmp := RGBTripleToColor(Row[x]);
        for i := 1 to GraphCount do
			    if Graph[i].Gradient and (tmp = Fill[i]) then
				    Row[x] := Grad[i];
      end;
	  end;
  end;
end;

function TCM2sGraph.GetGradient(StartColor, EndColor: TColor; PointValue, Min, Max: Single): TColor;
var
	F: Single;
	r1, r2, r3, g1, g2, g3, b1, b2, b3: Byte;
	function CalcColorBytes(fb1, fb2: Byte): Byte;
	begin
		result := fb1;
		if fb1 < fb2 then
			Result := FB1 + Trunc(F * (fb2 - fb1));
		if fb1 > fb2 then
			Result := FB1 - Trunc(F * (fb1 - fb2));
	end;
begin
	if PointValue <= Min then
  begin
		result := StartColor;
		exit;
	end;
	if PointValue >= Max then
  begin
		result := EndColor;
		exit;
	end;
	F := (PointValue - Min) / (Max - Min);
	asm
		mov EAX, Startcolor
		cmp EAX, EndColor
		je @@exit
		mov r1, AL
		shr EAX,8
		mov g1, AL
		shr Eax,8
		mov b1, AL
		mov Eax, EndColor
		mov r2, AL
		shr EAX,8
		mov g2, AL
		shr EAX,8
		mov b2, AL
		push ebp
		mov al, r1
		mov dl, r2
		call CalcColorBytes
		pop ecx
		push ebp
		Mov r3, al
		mov dL, g2
		mov al, g1
		call CalcColorBytes
		pop ecx
		push ebp
		mov g3, Al
		mov dL, B2
		mov Al, B1
		call CalcColorBytes
		pop ecx
		mov b3, al
		XOR EAX,EAX
		mov AL, B3
		SHL EAX,8
		mov AL, G3
		SHL EAX,8
		mov AL, R3
	@@Exit:
		mov @result, eax
	end;
end;

function TCM2sGraph.Update(SensorStyle: Byte; NewString: PChar; NewValue: Double): HResult;
var
  i, Num: Integer;
  tmp: String;
begin
	Result := S_OK;
	GraphCount := 1;
	Case SensorStyle of
		0: Graph[1].Value := NewValue;
		1: begin
      Num := CountFilt(NewString, '|');
      if Num > MaxCount then  //MaxCount
        Num := MaxCount;

      for i := 1 to Num do
      begin
        tmp := GetFilt(NewString, i, '|');
        if tmp <> '' then
        begin
          try
            Graph[i].Value := StrToFloat(tmp);
          except
            Graph[i].Value := gMin;
          end;
          GraphCount := i;
        end;
      end;
    end;
  else
    Result := E_Fail;
    LastErrorMsg := 'Invalid SensorStyle #' + IntToStr(SensorStyle);
	end;

  if GraphCount = 1 then
    CalcValues
  else
  begin
    History := (History + 1) Mod GraphCount;

    if History = 0 then
      CalcValues;
  end;
end;

Procedure TCM2sGraph.CalcValues;
var
	i, j, num, avg: integer;
begin
  for i := 1 to GraphCount do
	begin
		if Graph[i].Value <= gMin then
			num := 0
		else if Graph[i].Value >= Graph[i].Max then
			num := fHeight-1
		else
			num := Round((Graph[i].Value - gMin) / (Graph[i].Max - gMin) * fHeight);

    avg := 0;
    if BottomToTop then
    begin
      for j := 1 to gLength do
      begin
        Graph[i].Points[j].Y := Graph[i].Points[j+1].Y;
        avg := avg + (fHeight - Graph[i].Points[j+1].Y);
      end;     
      Graph[i].Points[gLength + 1].Y := fHeight - num;
    end else begin
      for j := 1 to gLength do
      begin
        Graph[i].Points[j].Y := Graph[i].Points[j+1].Y;
        avg := avg + Graph[i].Points[j+1].Y;
      end;  
      Graph[i].Points[gLength + 1].Y := num;
    end;

    Graph[i].Average := (avg + num) / (gLength + 1);
  end;
end;

function TCM2sGraph.Init(PaintDC: THandle): HResult;
var
  i: Byte;
begin
	Result := S_OK;
	try
		fBMP := TBitmap.Create;
		fBMP.PixelFormat := pf24bit;
		fDC := PaintDC;
	except              
		Result := E_FAIL;
		LastErrorMsg := 'Error in Init Function';
		Exit;
	end;
  for i := 1 to MaxCount do  //MaxCount
  begin
    Graph[i].Max := 100;
    Fill[i] := StringToColor('$001010' + IntToStr(10 + (i*2)));
  end;
  Graph[1].FillColor := clWhite;
  Graph[1].EndColor := clBlue;
  Graph[1].EdgeColor := clBlack;
  Graph[2].FillColor := clRed;
  Graph[2].EndColor := clWhite;
  Graph[2].EdgeColor := clBlue;
  Graph[3].FillColor := clGreen;
  Graph[3].EndColor := clTeal;
  Graph[3].EdgeColor := clNavy;
  Graph[4].FillColor := clBlue;
  Graph[4].EndColor := clNavy;
  Graph[4].EdgeColor := clWhite;
  GridBack := True;
  GridFront := False;
	GridColor := clBlue;
	GridStyle := psDashDotDot;
	GridX := 3;
	GridY := 3;
  GradDirection := 0;
	gMin := 0;
	BackgroundColor := clGray;
	BorderColor := clMaroon;
	BorderWidth := 1;
	Edge := True;
	EdgeStyle := psInsideFrame;
	EdgeWidth := 1;
	gLength := 20;
	BottomToTop := True;
	FontColor := clBlack;
	BarAvg := True;
	BarWidth := 8;
  ValueString := '%v';
  Padding := 0;
	InitGP;
end;    

procedure TCM2sGraph.InitGP;
var
	i, j: integer;
  space: Single;
begin
  History := 0;

  if BottomToTop then
  begin
    if BarGraph then
      StartPoint := fHeight
    else
      StartPoint := fHeight -1;
  end
    else
      StartPoint := 0;

	if (gLength + 1) >= fWidth then
		space := 1
	else
		space := fWidth / gLength;

  for i := 1 to MaxCount do  //MaxCount
    if i = 1 then
    begin
      SetLength(Graph[i].Points, gLength+3);

      Graph[i].Points[0] := Point(0, StartPoint);
      for j := 1 to gLength do
        Graph[i].Points[j] := Point(Round(Space*(j-1)), StartPoint);

      Graph[i].Points[gLength+1] := Point(fWidth-1, StartPoint);
      Graph[i].Points[gLength+2] := Point(fWidth-1, StartPoint);
    end
    else
      Graph[i].Points := Copy(Graph[1].Points);
end;

function TCM2sGraph.GetLastErrorMsg(OUT Msg: PChar): HResult;
begin
	If LastErrorMsg = '' then
		LastErrorMsg := 'No Errors';

	Msg := PChar(LastErrorMsg);
	Result := S_OK;
end;

function TCM2sGraph.GetDefault(Index: Integer; out Name, Value, Description: PChar): HResult;
begin
	Result := S_OK;
	Case Index of
		0: Begin Name := 'FillColor'; Value := 'White'; Description := 'Graph Fill Color'; end;
		1: Begin Name := 'FillStyle'; Value := 'Solid'; Description := 'Graph Fill Style'; end;
		2: Begin Name := 'BackgroundColor'; Value := 'Gray'; Description := 'Background Color'; End;
		3: Begin Name := 'Length'; Value := '20'; Description := 'Length of Graph History'; End;
		4: Begin Name := 'Min'; Value := '0'; Description := 'Minimum Input Value'; end;
		5: Begin Name := 'Max'; Value := '100'; Description := 'Maximum Input Value'; end;
		6: Begin Name := 'Bars'; Value := 'No'; Description := 'Bar Graphs instead of Filled Line Graph'; end; 
		7: Begin Name := 'Border'; Value := 'No'; Description := 'Show Borders'; end;
		8: Begin Name := 'Edge'; Value := 'Yes'; Description := 'Show Edge'; end;
  else
    LastErrorMsg := 'Default Index of Out of Bounds';
    Result := E_FAIL;
	end;
end;

function TCM2sGraph.GetDefaultCount(out DefaultCount: Integer): HResult;
begin
	Result := S_OK;
	DefaultCount := 9;
end;

function TCM2sGraph.GetOptional(Index: Integer; out Name, Value, Description: PChar): HResult;
begin
	Result := S_OK;
	Case Index of
		0: Begin Name := 'FillStyle2'; Value := 'Solid'; Description := 'Graph2 Fill Style'; end;
		1: Begin Name := 'FillStyle3'; Value := 'Solid'; Description := 'Graph3 Fill Style'; end;
		2: Begin Name := 'FillStyle4'; Value := 'Solid'; Description := 'Graph4 Fill Style'; end;
		3: Begin Name := 'FillColor2'; Value := 'Red'; Description := 'Graph2 Fill Color'; end;
		4: Begin Name := 'FillColor3'; Value := 'Green'; Description := 'Graph3 Fill Color'; end;
		5: Begin Name := 'FillColor4'; Value := 'Blue'; Description := 'Graph4 Fill Color'; end;
		6: Begin Name := 'EndColor'; Value := 'Blue'; Description := 'Graph Gradient End Color'; end;
		7: Begin Name := 'EndColor2'; Value := 'White'; Description := 'Graph2 Gradient End Color'; end;
		8: Begin Name := 'EndColor3'; Value := 'Teal'; Description := 'Graph3 Gradient End Color'; end;
		9: Begin Name := 'EndColor4'; Value := 'Navy'; Description := 'Graph4 Gradient End Color'; end;
		10: Begin Name := 'Direction'; Value := 'BottomToTop'; Description := 'Graph Direction'; end;
		11: Begin Name := 'BorderColor'; Value := 'Maroon'; Description := 'Border Color'; End;
		12: Begin Name := 'BorderWidth'; Value := '1'; Description := 'Border Width'; End;
		13: Begin Name := 'EdgeWidth'; Value := '1'; Description := 'Graph Edge Thickness'; end;
		14: Begin Name := 'EdgeStyle'; Value := 'Solid'; Description := 'Graph Edge Line Style'; end;
		15: Begin Name := 'EdgeColor'; Value := 'Black'; Description := 'Graph Edge Color'; end;
		16: Begin Name := 'EdgeColor2'; Value := 'Blue'; Description := 'Graph2 Edge Color'; end;
		17: Begin Name := 'EdgeColor3'; Value := 'Navy'; Description := 'Graph3 Edge Color'; end;
		18: Begin Name := 'EdgeColor4'; Value := 'White'; Description := 'Graph4 Edge Color'; end;
		19: Begin Name := 'ShowGrid'; Value := 'Back'; Description := 'Show Grid on Graph'; end;
		20: Begin Name := 'GridStyle'; Value := 'DashDotDot'; Description := 'Grid Line Style'; end;
		21: Begin Name := 'GridX'; Value := '3'; Description := 'Number of Lines for X-Axis'; end;
		22: Begin Name := 'GridY'; Value := '3'; Description := 'Number of Lines for Y-Axis'; end;
		23: Begin Name := 'GridColor'; Value := 'Blue'; Description := 'Color of Grid Lines'; end;
		24: Begin Name := 'Font'; Value := 'Arial,8,0000'; Description := 'Value Font Settings'; end;
		25: Begin Name := 'FontColor'; Value := 'Black'; Description := 'Value Font Color'; end;
		26: Begin Name := 'ShowValue'; Value := 'No'; Description := 'Show Input Value on Graph'; end;
		27: Begin Name := 'DrawMode'; Value := 'Average'; Description := 'Graph Drawing Order'; end;
		28: Begin Name := 'BarWidth'; Value := '8'; Description := 'Bar Width'; end;
		29: Begin Name := 'Max2'; Value := '100'; Description := 'Graph2 Maximum Input Value'; end;
		30: Begin Name := 'Max3'; Value := '100'; Description := 'Graph3 Maximum Input Value'; end;
		31: Begin Name := 'Max4'; Value := '100'; Description := 'Graph4 Maximum Input Value'; end;
		32: Begin Name := 'ValueString'; Value := '%v'; Description := 'Format of Input Value on Graph, use %v for the Value'; end;
		33: Begin Name := 'ValuePadding'; Value := '0'; Description := 'Pad the Input Value with Zeros to a Fixed Length, eg Pad 4 = 0023'; end; 
		34: Begin Name := 'Gradient'; Value := 'TopToBottom'; Description := 'Direction to Draw Gradient'; end;
  else
    Result := E_FAIL;
    LastErrorMsg := 'Optional Index Out of Bounds';
	end;
end;

function TCM2sGraph.GetOptionalCount(out OptionalCount: Integer): HResult;
begin
	OptionalCount := 35;
	Result := S_OK;
end;

function TCM2sGraph.SetConfigString(NewConfigString: PChar): HResult;
const                          
  GridLine = 0;
  EdgeLine = 1;
	arBrush : Array [1..14] of String = ('GRADIENT', 'CLEAR', 'HORIZONTAL', 'GRADIENT-HORIZONTAL', 'VERTICAL', 'GRADIENT-VERTICAL',
    'FDIAGONAL', 'GRADIENT-FDIAGONAL', 'BDIAGONAL', 'GRADIENT-BDIAGONAL', 'CROSS', 'GRADIENT-CROSS', 'DIAGCROSS', 'GRADIENT-DIAGCROSS');
	arPen : Array[1..4] of String = ('DASH', 'DOT', 'DASHDOT', 'DASHDOTDOT');

  procedure SetFillStyle(Param: String; Index: Byte);
  var
    i, Val : Integer;
    FS: TBrushStyle;
  begin
    Val := 0;
    for i := 1 to 14 do
      if Param = arBrush[i] then
      begin
        Val := i;
        break;
      end;
                  
    Graph[Index].Gradient := False;
    Case Val of
      1: begin
        FS := bsSolid;
        Graph[Index].Gradient := True;
      end;
      2: FS := bsClear;
      3: FS := bsHorizontal;
      4: begin
        FS := bsHorizontal;
        Graph[Index].Gradient := True;
      end;
      5: FS := bsVertical;
      6: begin
        FS := bsVertical;
        Graph[Index].Gradient := True;
      end;
      7: FS := bsFDiagonal;
      8: begin
        FS := bsFDiagonal;
        Graph[Index].Gradient := True;
      end;
      9: FS := bsBDiagonal;
      10: begin
        FS := bsBDiagonal;
        Graph[Index].Gradient := True;
      end;
      11: FS := bsCross;
      12: begin
        FS := bsCross;
        Graph[Index].Gradient := True;
      end;
      13: FS := bsDiagCross;
      14: begin
        FS := bsDiagCross;
        Graph[Index].Gradient := True;
      end;
    else
      FS := bsSolid;
    end;

    Graph[Index].bStyle := FS;
  end;
  procedure SetLineStyle(Param: String; Line: Byte);
  var
    i, Val : integer;
  begin
    Val := 0;
    for i := 1 to 4 do
      if Param = arPen[i] then
      begin
        Val := i;
        break;
      end;

    Case Line of
      GridLine:
        Case Val of
          1: GridStyle := psDash;
          2: GridStyle := psDot;
          3: GridStyle := psDashDot;
          4: GridStyle := psDashDotDot;
        else
          GridStyle := psSolid;
        end;
      EdgeLine:
        Case Val of
          1: EdgeStyle := psDash;
          2: EdgeStyle := psDot;
          3: EdgeStyle := psDashDot;
          4: EdgeStyle := psDashDotDot;
        else
          EdgeStyle := psInsideFrame;
        end;
    end;
  end;
var
	i: Integer;
	S, N, V, FStyle: String;
begin
	Result := S_OK;
	S := NewConfigString;
	For i := 1 to CountFilt(NewConfigString, '|') do
	begin
		S := GetFilt(NewConfigString, i, '|');
		N := Uppercase(GetFilt(S, 1, '='));
		V := Uppercase(GetFilT(S, 2, '='));

		If N = 'LENGTH' Then
		Begin
			try
				gLength := StrToInt(V)
			except
				gLength := 20;
			end;
			Continue;
		End;
		If (N = 'FILLCOLOR') or (N = 'FILLCOLOR1') Then
		Begin
			Try
				Graph[1].FillColor := StringToTColor(V);
			except
				Graph[1].FillColor := clWhite;
			end;
			Continue;
		End;
		If (N = 'ENDCOLOR') or (N = 'ENDCOLOR1' ) Then
		Begin
			Try
				Graph[1].EndColor := StringToTColor(V);
			except
				Graph[1].EndColor := clBlue;
			end;
			Continue;
		End;        
		If (N = 'EDGECOLOR') or (N = 'EDGECOLOR1')Then
		Begin
			Try
				Graph[1].EdgeColor := StringToTColor(V);
			except
				Graph[1].EdgeColor := clBlack;
			end;
			Continue;
		End;
		If (N = 'FILLSTYLE') or (N = 'FILLSTYLE1') Then
		Begin
      Graph[1].Gradient := False;
			if (V = 'SOLID') then
				Graph[1].bStyle := bsSolid
      else
        SetFillStyle(V, 1);
			Continue;
		End;    
		If (N = 'MAX') or (N = 'MAX1') Then
		Begin
			try
				Graph[1].Max := StrToInt(V);
			except
				Graph[1].Max := 100;
			end;
			Continue;
		End;
		If N = 'MIN' Then
		Begin
			try
				gMin := StrToInt(V);
			except
				gMin := 0;
			end;
			Continue;
		End;
		If (N = 'EDGE') or (N = 'EDGES') Then
		Begin
			If (V = 'YES') or (V = 'TRUE') Then
				Edge := True
			else
				Edge := False;
			Continue;
		End;         
		If (N = 'BARS') or (N = 'BAR') Then
		Begin
			If (V = 'YES') or (V = 'TRUE') Then
				BarGraph := True
			else
				BarGraph := False;
			Continue;
		End;
		If N = 'SHOWVALUE' Then
		Begin
			If (V = 'YES') or (V = 'TRUE') or (V = 'TOPLEFT') or (V = 'TOPRIGHT') or (V = 'BOTTOMLEFT') or (V = 'BOTTOMRIGHT') then
      begin
				ShowValue := True;
        if Pos('RIGHT', V) > 0 then
          TextRight := True
        else
          TextRight := False;
        if Pos('BOTTOM', V) > 0 then
          TextBottom := True
        else
          TextBottom := False;
      end
        else
				  ShowValue := False;
			Continue;
		End;
		If (N = 'DIRECTION') or (N = 'MODE') Then
		Begin
			If (V = 'TOPTOBOTTOM') Then
				BottomToTop := False
			else
				BottomToTop := True;
			Continue;
		End;          
		If N = 'DRAWMODE' Then
		Begin    
      BarAvg := True;
      InputOrder := False;
      RevOrder := False;
			If V = 'SMALLERINFRONT' Then
				BarAvg := False
			else if (V = 'INPUTORDER') or (V = 'INPUT') then
        InputOrder := True
      else if (V = 'REVERSEORDER') or (V = 'REVERSE') then
        RevOrder := True;
			Continue;
		End;         
		If N = 'GRADIENT' Then
		Begin
			if (V = 'BOTTOMTOTOP') or (V = 'B2T') or (V = '1') then
        GradDirection := 1
			else if (V = 'LEFTTORIGHT') or (V = 'L2R') or (V = '2') then
        GradDirection := 2
      else if (V = 'RIGHTTOLEFT') or (V = 'R2L') or (V = '3') then
        GradDirection := 3
      else
        GradDirection := 0;
			Continue;
		End;
		If N = 'BARWIDTH' Then
		Begin
			try
				BarWidth := StrToInt(V);
			except
				BarWidth := 8;
			end;
			Continue;
		End;
		If N = 'EDGESTYLE' Then
		Begin
			if (V = 'SOLID') then
				EdgeStyle := psInsideFrame
			else
				SetLineStyle(V, EdgeLine);
			Continue;
		End;
		If N = 'EDGEWIDTH' Then
		Begin
			try
				EdgeWidth := StrToInt(V);
			except
				EdgeWidth := 1;
			end;
			Continue;
		End;
		If (N = 'BACKGROUNDCOLOR') or (N = 'BACKGROUND') Then
		begin
			Try
				BackgroundColor := StringToTColor(V);
			except
				BackgroundColor := clGray;
			end;
			Continue;
		end;
		If (N = 'BORDER') or (N = 'BORDERS') Then
		Begin
			If (V = 'YES') or (V = 'TRUE') Then
				Border := True
			else
				Border := False;
			Continue;
		End;
		If N = 'BORDERCOLOR' Then
		begin
			Try
				BorderColor := StringToTColor(V);
			except
				BorderColor := clMaroon;
			end;
			Continue;
		end;
		If N = 'BORDERWIDTH' Then
		Begin
			try
				BorderWidth := StrToInt(V);
			except
				BorderWidth := 1;
			end;
			Continue;
		End;
		If N = 'SHOWGRID' Then
		Begin
      GridBack := False;
      GridFront := False;
			If (V = 'YES') or (V = 'TRUE') or (V = 'BACK') then
				GridBack := True
      else if V = 'FRONT' then
        GridFront := True;
			Continue;
		End;
		If N = 'GRIDCOLOR' Then
		begin
			Try
				GridColor := StringToTColor(V);
			except
				GridColor := clBlue;
			end;
			Continue;
		end;
		If N = 'GRIDSTYLE' Then
		Begin
			if (V = 'SOLID') then
				GridStyle := psSolid
			else
				SetLineStyle(V, GridLine);
			Continue;
		End;
		If N = 'GRIDX' Then
		Begin
			if (V = '0') or (V = 'NO') or (V = 'NONE') then
				GridX := 0
			else
				try
					GridX := StrToInt(V)
				except
					GridX := 3;
				end;
			Continue;
		End;
		If N = 'GRIDY' Then
		Begin
			if (V = '0') or (V = 'NO') or (V = 'NONE') then
				GridY := 0
			else
				try
					GridY := StrToInt(V)
				except
					GridY := 3;
				end;
			Continue;
		End;
		If N = 'FONT' Then
		Begin
			If CountFilt(V, ',') = 3 then
			begin
				try
					FontName := GetFilt(V, 1, ',');
				except
					FontName := 'Arial';
				End;
				try
					FontSize := StrToInt(GetFilt(V, 2, ','));
				except
					FontSize := 8;
				end;
				FStyle := GetFilt(V, 3, ',');
				If Length(FStyle) <> 4 then
					FontStyle := []
				else
				begin
					FontStyle := [];
					If FStyle[1] = '1' then
						FontStyle := [fsBold];
					If FStyle[2] = '1' then
						FontStyle := FontStyle + [fsItalic];
					If FStyle[3] = '1' then
						FontStyle := FontStyle + [fsUnderline];
					If FStyle[4] = '1' then
						FontStyle := FontStyle + [fsStrikeOut];
				end;
			end;
			Continue;
		End;
		If N = 'FONTCOLOR'then
		begin
			Try
				FontColor := StringToTColor(V);
			except
				FontColor := clBlack;
			end;
			Continue;
		End; 
		If (N = 'VALUESTRING') or (N = 'VALUE') Then
    begin
      ValueString := GetFilt(S, 2, '=');
      Continue;
    end;
    if (N = 'VALUEPADDING') or (N = 'PADDING') or (N = 'PAD') then
    begin
      try
        Padding := StrToInt(V);
      except
        Padding := 0;
      end;
      Continue;
    end;
		If (N = 'FILLCOLOR2') or (N = 'FILLCOLOR3') or (N = 'FILLCOLOR4') or (N = 'FILLCOLOR5')
      or (N = 'FILLCOLOR6') or (N = 'FILLCOLOR7') or (N = 'FILLCOLOR8') Then
		Begin
			Try
				Graph[StrToInt(N[Length(N)])].FillColor := StringToTColor(V);
			except
				Graph[StrToInt(N[Length(N)])].FillColor := clRed;
			end;
			Continue;
		End;   
		If (N = 'ENDCOLOR2') or (N = 'ENDCOLOR3') or (N = 'ENDCOLOR4') or (N = 'ENDCOLOR5')
      or (N = 'ENDCOLOR6') or (N = 'ENDCOLOR7') or (N = 'ENDCOLOR8') Then
		Begin
			Try
				Graph[StrToInt(N[Length(N)])].EndColor := StringToTColor(V);
			except
				Graph[StrToInt(N[Length(N)])].EndColor := clWhite;
			end;
			Continue;
		End;
		If (N = 'EDGECOLOR2') or (N = 'EDGECOLOR3') or (N = 'EDGECOLOR4') or (N = 'EDGECOLOR5')
      or (N = 'EDGECOLOR6') or (N = 'EDGECOLOR7') or (N = 'EDGECOLOR8') Then
		Begin
			Try
				Graph[StrToInt(N[Length(N)])].EdgeColor := StringToTColor(V);
			except
				Graph[StrToInt(N[Length(N)])].EdgeColor := clBlue;
			end;
			Continue;
		End;
		If (N = 'MAX2') or (N = 'MAX3') or (N = 'MAX4') or  (N = 'MAX5') or (N = 'MAX6') or (N = 'MAX7')or (N = 'MAX8') Then
		Begin
			try
				Graph[StrToInt(N[Length(N)])].Max := StrToInt(V);
			except
				Graph[StrToInt(N[Length(N)])].Max := 100;
			end;
			Continue;
		End;   
		If (N = 'FILLSTYLE2') or (N = 'FILLSTYLE3') or (N = 'FILLSTYLE4') or (N = 'FILLSTYLE5')
      or (N = 'FILLSTYLE6') or (N = 'FILLSTYLE7') or (N = 'FILLSTYLE8') Then
		Begin
      Graph[StrToInt(N[Length(N)])].Gradient := False;
			if (V = 'SOLID') then
				Graph[StrToInt(N[Length(N)])].bStyle := bsSolid
      else
        SetFillStyle(V, StrToInt(N[Length(N)]));
			Continue;
		End;
	End;
	InitGP;
end;

function TCM2sGraph.SetDC(const NewDC: THandle): HResult;
begin
	fDC := NewDC;
	Result := S_OK;
end;

function TCM2sGraph.SetDimensions(Left, Top, Width, Height: Integer): HResult;
begin            
	Result := S_OK;
	fLeft := Left;
	fTop := Top;
	fWidth := Width;
	fHeight := Height;
end;

function TCM2sGraph.Setup: HResult;
begin
	Result := E_NOTIMPL;
end;

function TCM2sGraph.Terminating: HResult;
begin
	Result := S_OK;
	FreeAndNil(fBMP);
end;

function TCM2sGraph.ObjIns(ParameterName: PChar; out ValueType: Byte; out ValueRange: PChar): HResult;
var
	N: String;
begin
	Result := S_OK;
	N := UpperCase(ParameterName);
	ValueType := vtUnknown;
	ValueRange := '';
	If N = 'FONT' then
	begin
		ValueType := vtFontEx;
		exit;
	end;
	If N = 'FONTCOLOR' then
	begin
		ValueType := vtColor;
		exit;
	end;
	If (N = 'FILLCOLOR') or (N = 'FILLCOLOR1') or (N = 'FILLCOLOR2') or (N = 'FILLCOLOR3') or (N = 'FILLCOLOR4')
    or (N = 'FILLCOLOR5') or (N = 'FILLCOLOR6') or (N = 'FILLCOLOR7') or (N = 'FILLCOLOR8') then
	begin
		ValueType := vtColor;
		exit;
	end;
	If (N = 'ENDCOLOR') or (N = 'ENDCOLOR1') or (N = 'ENDCOLOR2') or (N = 'ENDCOLOR3') or (N = 'ENDCOLOR4')
    or (N = 'ENDCOLOR5') or (N = 'ENDCOLOR6') or (N = 'ENDCOLOR7') or (N = 'ENDCOLOR8') then
	begin
		ValueType := vtColor;
		exit;
	end;
	If N = 'SHOWGRID' then
	begin
		ValueType := vtFixed;
		ValueRange := 'Back|Front|No';
		exit;
	end;                   
	If N = 'GRADIENT' then
	begin
		ValueType := vtFixed;
		ValueRange := 'TopToBottom|BottomToTop|LeftToRight|RightToLeft';
		exit;
	end;
	If N = 'SHOWVALUE' then
	begin
		ValueType := vtFixed;
		ValueRange := 'No|TopLeft|TopRight|BottomLeft|BottomRight';
		exit;
	end;
	If (N = 'EDGE') or (N = 'EDGES') then
	begin
		ValueType := vtFixed;
		ValueRange := 'Yes|No';
		exit;
	end;   
	If (N = 'BARS') or (N = 'BAR') then
	begin
		ValueType := vtFixed;
		ValueRange := 'Yes|No';
		exit;
	end;
	If (N = 'DIRECTION') or (N = 'MODE') then
	begin
		ValueType := vtFixed;
		ValueRange := 'BottomToTop|TopToBottom';
		exit;
	end;     
	If N = 'DRAWMODE' then
	begin
		ValueType := vtFixed;
		ValueRange := 'Average|SmallerInFront|InputOrder|ReverseOrder';
		exit;
	end;
	If (N = 'EDGECOLOR') or (N = 'EDGECOLOR1') or (N = 'EDGECOLOR2') or (N = 'EDGECOLOR3') or (N = 'EDGECOLOR4')
    or (N = 'EDGECOLOR5') or (N = 'EDGECOLOR6') or (N = 'EDGECOLOR7') or (N = 'EDGECOLOR8') then
	begin
		ValueType := vtColor;
		exit;
	end;
	If (N = 'FILLSTYLE') or (N = 'FILLSTYLE1') or (N = 'FILLSTYLE2') or (N = 'FILLSTYLE3') or (N = 'FILLSTYLE4')
    or (N = 'FILLSTYLE5') or (N = 'FILLSTYLE6') or (N = 'FILLSTYLE7') or (N = 'FILLSTYLE8') then
	begin
		ValueType := vtFixed;
		ValueRange := 'Solid|Gradient|Clear|Horizontal|Gradient-Horizontal|Vertical|Gradient-Vertical|FDiagonal|Gradient-FDiagonal|BDiagonal|Gradient-BDiagonal|Cross|Gradient-Cross|DiagCross|Gradient-DiagCross';
		exit;
	end;
	If (N = 'BORDER') or (N = 'BORDERS') then
	begin
		ValueType := vtFixed;
		ValueRange := 'Yes|No';
		exit;
	end;
	If N = 'BORDERCOLOR' then
	begin
		ValueType := vtColor;
		exit;
	end;
	If N = 'GRIDCOLOR' then
	begin
		ValueType := vtColor;
		exit;
	end;
	If N = 'GRIDSTYLE' then
	begin
		ValueType := vtFixed;
		ValueRange := 'Solid|Dash|Dot|DashDot|DashDotDot';
		exit;
	end;                     
	If N = 'EDGESTYLE' then
	begin
		ValueType := vtFixed;
		ValueRange := 'Solid|Dash|Dot|DashDot|DashDotDot';
		exit;
	end;
	If (N = 'BACKGROUNDCOLOR') or (N = 'BACKGROUND') then
		ValueType := vtColor;
end;

initialization
	TComObjectFactory.Create(ComServer, TCM2sGraph, Class_CM2sGraph,
		'Vis_sGraph', 'CoolMon 2 Solid Graph Plugin', ciMultiInstance, tmApartment);
end.
