/*
 * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * AELITIS, SAS au capital de 46,603.30 euros
 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
 */
package org.gudy.azureus2.ui.swt.mainwindow;


public class HSLColor {
  private final static int HSLMAX = 255;
  private final static int RGBMAX = 255;
  private final static int UNDEFINED = 170;

  private int pHue;
  private int pSat;
  private int pLum;
  private int pRed;
  private int pGreen;
  private int pBlue;

  public void initHSLbyRGB(int R, int G, int B) {
    // sets Hue, Sat, Lum
    int cMax;
    int cMin;
    int RDelta;
    int GDelta;
    int BDelta;
    int cMinus;
    int cPlus;

    pRed = R;
    pGreen = G;
    pBlue = B;

    //Set Max & MinColor Values
    cMax = iMax(iMax(R, G), B); 
    cMin = iMin(iMin(R, G), B);

    cMinus = cMax - cMin;
    cPlus = cMax + cMin;

    // Calculate luminescence (lightness)
    pLum = ((cPlus * HSLMAX) + RGBMAX) / (2 * RGBMAX);

    if (cMax == cMin) {
      // greyscale
      pSat = 0;
      pHue = UNDEFINED;
    } else {
      // Calculate color saturation
      if (pLum <= (HSLMAX / 2)) {
        pSat = (int)(((cMinus * HSLMAX) + 0.5) / cPlus);
      } else {
        pSat = (int)(((cMinus * HSLMAX) + 0.5) / (2 * RGBMAX - cPlus));
      }

      //Calculate hue
      RDelta = (int)((((cMax - R) * (HSLMAX / 6)) + 0.5) / cMinus);
      GDelta = (int)((((cMax - G) * (HSLMAX / 6)) + 0.5) / cMinus);
      BDelta = (int)((((cMax - B) * (HSLMAX / 6)) + 0.5) / cMinus);

      if (cMax == R) {
        pHue = BDelta - GDelta;
      } else if (cMax == G) {
        pHue = (HSLMAX / 3) + RDelta - BDelta;
      } else if (cMax == B) {
        pHue = ((2 * HSLMAX) / 3) + GDelta - RDelta;
      }

      if (pHue < 0) {
        pHue = pHue + HSLMAX;
      }
    }
  }

  public void initRGBbyHSL(int H, int S, int L) {
    int Magic1;
    int Magic2;

    pHue = H;
    pLum = L;
    pSat = S;

    if (S == 0) { //Greyscale
      pRed = (L * RGBMAX) / HSLMAX; //luminescence: set to range
      pGreen = pRed;
      pBlue = pRed;
    } else {
      if (L <= HSLMAX / 2) {
        Magic2 = (L * (HSLMAX + S) + (HSLMAX / 2)) / (HSLMAX);
      } else {
        Magic2 = L + S - ((L * S) + (HSLMAX / 2)) / HSLMAX;
      }
      Magic1 = 2 * L - Magic2;

      //get R, G, B; change units from HSLMAX range to RGBMAX range
      pRed = (hueToRGB(Magic1, Magic2, H + (HSLMAX / 3)) * RGBMAX + (HSLMAX / 2)) / HSLMAX;
      if (pRed > RGBMAX) { 
        pRed = RGBMAX;
      }

      pGreen = (hueToRGB(Magic1, Magic2, H) * RGBMAX + (HSLMAX / 2)) / HSLMAX;
      if (pGreen > RGBMAX) { 
        pGreen = RGBMAX;
      }

      pBlue = (hueToRGB(Magic1, Magic2, H - (HSLMAX / 3)) * RGBMAX + (HSLMAX / 2)) / HSLMAX;
      if (pBlue > RGBMAX) { 
        pBlue = RGBMAX;
      }
    }
  }

  private int hueToRGB(int mag1, int mag2, int Hue) {
    // check the range
    if (Hue < 0) {
      Hue = Hue + HSLMAX;
    } else if (Hue > HSLMAX) {
      Hue = Hue - HSLMAX;
    }

    if (Hue < (HSLMAX / 6))
      return (mag1 + (((mag2 - mag1) * Hue + (HSLMAX / 12)) / (HSLMAX / 6)));
      
    if (Hue  < (HSLMAX / 2))
     return mag2;
     
    if (Hue < (HSLMAX * 2 / 3))
      return (mag1 + (((mag2 - mag1) * ((HSLMAX * 2 / 3) - Hue) + (HSLMAX / 12)) / (HSLMAX / 6)));

    return mag1;
  }

  private int iMax(int a, int b) {
    if (a > b) return a; else return b;
  }
  private int iMin(int a, int b) {
    if (a < b) return a; else return b;
  }



  public void greyscale() {
    initRGBbyHSL(UNDEFINED, 0, pLum);
  }


  // --

  public int getHue() {
    return pHue;
  }

  public void setHue(int iToValue) {
    while (iToValue < 0) {
      iToValue = HSLMAX + iToValue;
    }
    while (iToValue > HSLMAX) {
      iToValue = iToValue - HSLMAX;
    }

    initRGBbyHSL(iToValue, pSat, pLum);
  }

  // --

  public int getSaturation() {
    return pSat;
  }

  public void setSaturation(int iToValue) {
    if (iToValue < 0) {
      iToValue = 0;
    } else if (iToValue > HSLMAX) {
      iToValue = HSLMAX;
    }

    initRGBbyHSL(pHue, iToValue, pLum);
  }

  // --

  public int getLuminence() {
    return pLum;
  }

  public void setLuminence(int iToValue) {
    if (iToValue < 0) {
      iToValue = 0;
    } else if (iToValue > HSLMAX) {
      iToValue = HSLMAX;
    }

    initRGBbyHSL(pHue, pSat, iToValue);
  }

  // --

  public int getRed() {
    return pRed;
  }

  public void setRed(int iNewValue) {
    initHSLbyRGB(iNewValue, pGreen, pBlue);
  }

  // --

  public int getGreen() {
    return pGreen;
  }

  public void setGreen(int iNewValue) {
    initHSLbyRGB(pRed, iNewValue, pBlue);
  }

  // --

  public int getBlue() {
    return pBlue;
  }

  public void setBlue(int iNewValue) {
    initHSLbyRGB(pRed, pGreen, iNewValue);
  }

  // --

  public void reverseColor() {
    setHue(pHue + (HSLMAX / 2));
  }

  // --

  public void reverseLight() {
    setLuminence(HSLMAX - pLum);
  }

  // --

  public void brighten(float fPercent) {
    int L;

    if (fPercent == 0) {
      return;
    }

    L = (int)(pLum * fPercent);
    if (L < 0) L = 0;
    if (L > HSLMAX) L = HSLMAX;

    setLuminence(L);
  }

  // --
  // --

  public void blend(int R, int G, int B, float fPercent) {
    if (fPercent >= 1) {
      initHSLbyRGB(R, G, B);
      return;
    }
    if (fPercent <= 0)
      return;

    int newR = (int)((R * fPercent) + (pRed * (1.0 - fPercent)));
    int newG = (int)((G * fPercent) + (pGreen * (1.0 - fPercent)));
    int newB = (int)((B * fPercent) + (pBlue * (1.0 - fPercent)));

    initHSLbyRGB(newR, newG, newB);
  }
}
