/* ****************************************************************************************** *
 * Das Script kann frei verwendet werden, dieser Kommentar sowie die Nennung des Nicks
 * und der URL müssen jedoch erhalten bleiben.
 *
 *                                                           (c) Quaese (www.quaese.de), 2009
 * ****************************************************************************************** */

Object.prototype.extendOpts = function(objOpts){
	for(strEntry in objOpts)
  	this[strEntry] = objOpts[strEntry];
}

function qpCanvas(objOpts){
	/* *** [DEFAULTWERTE FÜR EIGENSCHAFTEN] *** */
	this.id          = 'canvas_id';  // ID des Canvas-Elements
  this.width       = 300;          // Breite des Canvas-Elements
  this.height      = 300;          // Höhe des Canvas-Elements
  this.bgFull      = '#fff';       // Hintergrundfarbe kompletten Kreis
  this.bgMid       = '#fff';       // Hintergrundfarbe innerer Kreis
  this.bgFullShow  = false;        // Hintergrund hinter komplettem Kreis (bis aussen)
  this.bgMidShow   = false;        // Hintergrund in Mitte
  this.segments    = 30;           // Anzahl der Segmente, die im Kreis angezeigt werden sollen
  this.lineWidth   = 0.0;          // Linienstärke
  this.segspace    = 0.15;         // Space zwischen den Segmenten: 0.0 .. 1.0
  this.classname   = null;         // CSS-Klassenname für Canvas-Element
  this.center      = false;        // Zentriert das Canvas-Element auf dem Bildschirm
  this.onStart     = null;         // Callbackfkt, die vor dem periodischen Abarbeiten aufgerufen wird
  this.onComplete  = null;         // Callbackfkt, die nach dem periodischen Abarbeiten aufgerufen wird
  this.onAfterStep = null;         // Callbackfkt, die nach jedem periodischen Schritt ausgeführt wird


  // Übergebene Optionen an Eigenschaften der Klasse übergeben
  this.extendOpts(objOpts);

  // Canvasobjekt, Context-Eigenschaft, Timerhandle
  this.canvas     = null;
  this.context    = null;
  this.hTimer     = null;

  // Optionen testen und ggf. korrigieren
  this.checkOpts();

  // Rahmenfarbe der Hintergrundfarbe anpassen
  this.borderColor = this.bgFull; //  "rgba(0,0,0,0)";

  // Browser unterstützt Verknüpfungen (true/false = ja/nein)
  this.composites = false;

  // Radius
  this.radius = {
  	x: Math.round(this.width/2),
  	y: Math.round(this.height/2)
  };

  this.init();
}


/*
 * Initialisiert das Canvas-Objekt
 */
qpCanvas.prototype.init = function(){

  this.canvas = document.getElementById(this.id);

  this.canvas.width = this.width;
  this.canvas.height = this.height;
  this.canvas.style.width = this.width + "px";
  this.canvas.style.height = this.height + "px";
  // Falls das Objekt unterstützt wird
  if(this.canvas.getContext){
    // Kontext
    this.context = this.canvas.getContext("2d");

	  // Falls der Browser Verknüpfungen unterstützt
	  this.composites = (typeof this.context.globalCompositeOperation != 'undefined')? true : false;

    // Falls eine CSS-Klasse zum Formatieren des Canvaselements existiert
    if(this.classname != null)
    	this.canvas.className = this.classname;
    if(this.center)
	    // Styles für Canvas-Element
	    this.setStyle();
  }else{
    // Sonstiger Code
  }
}


/*
 * Testet die Eigenschaften (Optionen) und korrigiert diese ggf.
 */
qpCanvas.prototype.checkOpts = function(){
  this.width       = (isNaN(this.width))? 300 : ((this.width<0)? (-1)*this.width : this.width);
  this.height      = (isNaN(this.height))? 300 : ((this.height<0)? (-1)*this.height : this.height);
  this.bgFull      = (this.setRGBA(this.bgFull, 1.0))? this.setRGBA(this.bgFull, 1.0) : '#fff';
  this.bgMid       = (this.setRGBA(this.bgMid, 1.0))? this.setRGBA(this.bgMid, 1.0) : '#fff';
  this.lineWidth   = (isNaN(this.lineWidth) || (this.lineWidth<=0))? 0 : this.lineWidth;
  this.segments    = (isNaN(this.segments) || (this.segments<=0))? 30 : this.segments;
  this.segspace    = (isNaN(this.segspace) || (this.segspace<0) || (this.segspace>1))? 0.15 : this.segspace;
  this.onStart     = (typeof this.onStart == 'function')? this.onStart : null;
  this.onComplete  = (typeof this.onComplete == 'function')? this.onComplete : null;
  this.onAfterStep = (typeof this.onAfterStep == 'function')? this.onAfterStep : null;
}


/*
 * Setzt das Canvas-Objekt absolut in die Seitenmitte
 */
qpCanvas.prototype.setStyle = function(){
	this.canvas.style.position   = "absolute";
  this.canvas.style.top        = "50%";
  this.canvas.style.left       = "50%";
  this.canvas.style.zIndex     = 9999;
  this.canvas.style.marginTop  = -Math.round(this.height/2) + "px";
  this.canvas.style.marginLeft = -Math.round(this.width/2) + "px";
}


/*
 * Zeigt das Canvas-Element an
 */
qpCanvas.prototype.show = function(){
	this.canvas.style.display = "block";
}


/*
 * Versteckt das Canvas-Element
 */
qpCanvas.prototype.hide = function(){
	this.canvas.style.display = "none";
}


/*
 * Löscht den Inhalt des Canvas-Elements
 */
qpCanvas.prototype.clear = function(){
  this.context.clearRect(0, 0, this.width, this.height);
}


/*
 * Stösst das Zeichnen eines Kreises in einem übergebenen Zeitintervall mit einer übergebenen
 * Anzahl an Schritten an.
 *
 * intSec    - Anzahl der Sekunden, die für einen Umlauf vergehen sollen
 * intSteps  - Anzahl der Schritte, die für einen Umlauf ausgeführt werden sollen
 * intOuterR - Äusserer Radius
 * intInnerR - Innerer Radius
 */
qpCanvas.prototype.drawCircleByTimeAndSteps = function(intSec, intSteps, intOuterR, intInnerR){
	if(intOuterR < intInnerR){
  	var intHelp = intOuterR;
    intOuterR = intInnerR;
    intInnerR = intHelp;
  }

	// Millisekunden
  intSec *= 1000;
  // Zeit pro Schritt
  var intStepTime = Math.floor(intSec/intSteps);
  // Prozentuale Änderung pro Schritt
  var intProzDelta = Math.round(100/intSteps);

  // Callback aufrufen
  if(typeof this.onStart == 'function')
    this.onStart();

  this.startPeriodicalFx(0, intProzDelta, intStepTime, intOuterR, intInnerR);
}


/*
 * Stösst das Zeichnen eines Kreises in einem übergebenen Zeitintervall mit einer übergebenen
 * Anzahl an Schritten an.
 *
 * intSec    - Anzahl der Sekunden, die für einen Umlauf vergehen sollen
 * intSteps  - Anzahl der Schritte, die für einen Umlauf ausgeführt werden sollen
 * intOuterR - Äusserer Radius
 * intInnerR - Innerer Radius
 */
qpCanvas.prototype.drawCircleByPercent = function(intStartProz, intProzDelta, intStepTime, intOuterR, intInnerR){
	if(intOuterR < intInnerR){
  	var intHelp = intOuterR;
    intOuterR = intInnerR;
    intInnerR = intHelp;
  }

  // Callback aufrufen
  if(typeof this.onStart == 'function')
    this.onStart();

  this.startPeriodicalFx(intStartProz, intProzDelta, intStepTime, intOuterR, intInnerR);
}


/*
 * Starten einer periodischen Funktion
 */
qpCanvas.prototype.startPeriodicalFx = function(intProz, intProzDelta, intStepTime, r1, r2){
	var _this = this;

	this.drawSegmentCircle(intProz, r1, r2);
  //
	if(intProz < 100){
  	// Callback aufrufen
  	if(typeof this.onAfterStep == 'function')
    	this.onAfterStep(intProz);
  	this.hTimer = window.setTimeout(function(){_this.startPeriodicalFx(intProz+intProzDelta, intProzDelta, intStepTime, r1, r2);}, intStepTime);
  }else{
  	// Callback aufrufen
  	if(typeof this.onComplete == 'function')
    	this.onComplete();
  }
}


/*
 * intPercent - 0 .. 100
 */
qpCanvas.prototype.drawSegmentCircle = function(intPercent, intOuterRadius, intInnerRadius){
	if(intOuterRadius < intInnerRadius){
  	var intHelp = intOuterRadius;
    intOuterRadius = intInnerRadius;
    intInnerRadius = intHelp;
  }

  // Segmentdelta berechnen
  this.delta = 2*Math.PI/this.segments;
  // Segmentbogen berechnen (kleiner als this.delta, um Abstand zwischen Einzelsegmenten erzeugen zu können)
  this.delta2 = this.delta - this.segspace*this.delta;

  // Anzahl Segmente, die bei aktueller Prozentzahl gezeichnet werden müssen
  var intAnzSegs = Math.floor(this.segments*intPercent/100);

  // Anzeige löschen
  this.context.clearRect(0, 0, this.width, this.height);

  // Füllmethode Linien
  this.context.strokeStyle = this.borderColor;
  this.context.lineWidth   = this.lineWidth;

  this.context.globalCompositeOperation = 'source-over';

  // Falls ein Hintergrundkreis angezeigt werden soll
  if(this.bgFullShow){
  	// Hintergrundkreis zeichnen
  	this.drawCircle(this.radius.x, this.radius.y, intOuterRadius+1, this.bgFull);
  }

  // Benötigte Anzahl an Segmenten zeichnen
  for(var i=0; i<intAnzSegs; i++){
	  this.drawSegment(i, intOuterRadius);
	}

  // Falls der Browser Verknüpfungen unterstützt UND der innere Kreis nicht angezeigt werden soll
  if(this.composites && !this.bgMidShow)
	  // Verknüpfung so einstellen, dass innerer Kreis gelöscht wird
  	this.context.globalCompositeOperation = 'destination-out';

  // Innerern Kreis zeichnen
  this.drawCircle(this.radius.x, this.radius.y, intInnerRadius, this.bgMid);
}


/*
 * Zeichnet einen Kreis um den Mittelpunkt (intX, intY) mit dem Radius intRadius und der Füllfarbe strFillColor
 */
qpCanvas.prototype.drawCircle = function(intX, intY, intRadius, strFillColor){
  // Arbeitspfad zurücksetzen
  this.context.beginPath();
  // Startposition festlegen (gleich dem Mittelpkt)
  this.context.moveTo(intX, intY);
  this.context.arc(intX, intY, intRadius, 0, 2*Math.PI, false);
  this.context.closePath();
  this.context.fillStyle = strFillColor;
  this.context.fill();
}


/*
 * Zeichnet einen Kreisausschnitt
 */
qpCanvas.prototype.drawSegment = function(intSeg, intOuterRadius){
	// Neuen Pfad beginnen
  this.context.beginPath();
  // Startposition festlegen (gleich dem Mittelpkt)
  this.context.moveTo(this.radius.x, this.radius.y);
  // Farbe festlegen (dynamisch)
  this.context.fillStyle = "rgba(0, "+(255-intSeg*Math.round(120/this.segments))+", 0, 1.0)";
  // Kreisauschnitt zeichnen
  this.context.arc(this.radius.x, this.radius.y, intOuterRadius, -2*Math.PI/4+intSeg*(this.delta), -2*Math.PI/4+(intSeg)*(this.delta)+this.delta2, false);
  this.context.closePath();
  this.context.fill();
  this.context.stroke();
}



/* ****** [Funktion zum Umrechnen eines HexFarbStrings in Dezimalwerte] ******* *
 * Parameter: - strHex    -> Farbwert, der umgerechnet werden soll in           *
 *                           Hexadezimalschreibweise (#rrggbb)                  *
 * Rückgabe:  - Farbarray -> Komponente[0] = dezimaler Rot-Wert                 *
 *                           Komponente[1] = dezimaler Grün-Wert                *
 *                           Komponente[2] = dezimaler Blau-Wert                *
 *                                                                              *
 * Beispiel: HexToDez("#100fff");                                               *
 *           Liefert den Farbarray mit [0]=16, [1]=15, [2]=255                  *
 * **************************************************************************** */
//qpCanvas.prototype.HexToDec = function(strHex){
qpCanvas.prototype.setRGBA = function(strHex, dblOpac){
	// ****** [Array zur Gewichtung von HexZeichen] ********
  var arrHex = new Array();
  arrHex["0"] = 0;  arrHex["1"] = 1;  arrHex["2"] = 2;  arrHex["3"] = 3;
  arrHex["4"] = 4;  arrHex["5"] = 5;  arrHex["6"] = 6;  arrHex["7"] = 7;
  arrHex["8"] = 8;  arrHex["9"] = 9;  arrHex["a"] = 10; arrHex["b"] = 11;
  arrHex["c"] = 12; arrHex["d"] = 13; arrHex["e"] = 14; arrHex["f"] = 15;
  arrHex["A"] = 10; arrHex["B"] = 11; arrHex["C"] = 12; arrHex["D"] = 13;
  arrHex["E"] = 14; arrHex["F"] = 15;

  // Variable für Hilfsstring
  var strHelp = "";
  // Regulärer Ausdruck zum Testen der enthaltenen Zeichen
  // Genau 6 Zeichen erlaubt (Ziffern 0-9 oder Zeichen von A-F bzw. a-f)
  var strTest = /^[a-fA-F0-9]{6,6}$/;
  // Hilfsarray
  var arrHelp = new Array(3);

  // Falls Doppelraute im HexString enthalten
  if(strHex.indexOf("#") != -1){
    // Doppelrauten entfernen
    strHex = strHex.replace(/#/, "");
  }

  // Falls der Hex-String aus drei Zeichen besteht
  if(strHex.length == 3){
    strHex = strHex.charAt(0)+strHex.charAt(0)+strHex.charAt(1)+strHex.charAt(1)+strHex.charAt(2)+strHex.charAt(2);
  }

  // Falls ungültige Zeichen enthalten sind
  if(!strTest.test(strHex)){
    return false;
  }

  // Farbwerte ermitteln
  for(var i=0; i<3; i++){
    // Zeichenkette immer in 2-Zeichen-Portionen zerlegen
    strHelp = strHex.substr((i*2), 2);
    // Dezimalwert aus Teilstrings berechnen
    arrHelp[i] = arrHex[strHelp.charAt(0)]*16 + arrHex[strHelp.charAt(1)];
  }

  return "rgba("+arrHelp[0]+","+arrHelp[1]+","+arrHelp[2]+","+dblOpac+")";
}
