package com.intellij.ide.ui;

import java.awt.image.ImageFilter;

final class DaltonizationFilter extends WeightFilter {
  public static ImageFilter get(ColorBlindness blindness) {
    if (blindness == ColorBlindness.protanopia) return protanopia;
    if (blindness == ColorBlindness.deuteranopia) return deuteranopia;
    if (blindness == ColorBlindness.tritanopia) return tritanopia;
    return null;
  }

  public static final ImageFilter protanopia = forProtanopia(null);
  public static final ImageFilter deuteranopia = forDeuteranopia(null);
  public static final ImageFilter tritanopia = forTritanopia(null);

  public static ImageFilter forProtanopia(Double weight) {
    return new DaltonizationFilter("Protanopia", weight, 0, 2.02344, -2.52581, 0, 1, 0, 0, 0, 1);
  }

  public static ImageFilter forDeuteranopia(Double weight) {
    return new DaltonizationFilter("Deuteranopia", weight, 1, 0, 0, 0.494207, 0, 1.24827, 0, 0, 1);
  }

  public static ImageFilter forTritanopia(Double weight) {
    return new DaltonizationFilter("Tritanopia", weight, 1, 0, 0, 0, 1, 0, -0.395913, 0.801109, 0);
  }

  private final double[] myMatrix;

  private DaltonizationFilter(String name, Double weight, double... matrix) {
    super(name + " (daltonization)", weight);
    myMatrix = matrix;
  }

  @Override
  int toRGB(int srcR, int srcG, int srcB) {
    // RGB to LMS matrix conversion
    double L = (17.8824 * srcR) + (43.5161 * srcG) + (4.11935 * srcB);
    double M = (3.45565 * srcR) + (27.1554 * srcG) + (3.86714 * srcB);
    double S = (0.0299566 * srcR) + (0.184309 * srcG) + (1.46709 * srcB);
    // Simulate color blindness
    double l = L * myMatrix[0] + M * myMatrix[1] + S * myMatrix[2];
    double m = L * myMatrix[3] + M * myMatrix[4] + S * myMatrix[5];
    double s = L * myMatrix[6] + M * myMatrix[7] + S * myMatrix[8];
    // LMS to RGB matrix conversion
    double R = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
    double G = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
    double B = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
    // Isolate invisible colors to color vision deficiency (calculate error matrix)
    R = srcR - R;
    G = srcG - G;
    B = srcB - B;
    // Shift colors towards visible spectrum (apply error modifications)
    // and add compensation to original values
    double dstR = fix(srcR + (0.0 * R) + (0.0 * G) + (0.0 * B));
    double dstG = fix(srcG + (0.7 * R) + (      G) + (0.0 * B));
    double dstB = fix(srcB + (0.7 * R) + (0.0 * G) + (      B));
    return toRGB(srcR, srcG, srcB, dstR, dstG, dstB);
  }
}
