package yi.iu.imgdb.learn;

import java.io.*;
import java.util.*;

import java.awt.*;

import org.jmat.data.*;
import org.jmat.gui.*;
import yi.iu.processor.resample.*;
import yi.util.config.*;
import yi.util.io.*;

/**
 * This feature related utility class provides functions to generate image
 * features, to read feature object file from the disk, and to represent
 * features by feature vectors
 */

public class FeatureUtil
    implements Configurable {

  public int RESAMPLER = Resampler.NEAREST_RESAMPLER;

  public FeatureUtil() {
    Configuration.configParameters(this);
  }

  public String[] getParameterNames() {
    return new String[] {
        "RESAMPLER",
    };
  }

  /**
   * Generate the feature (FeatureRepresentable)
   * @return
   */
  public static FeatureRepresentable generate(
      String generator_name,
      String feature_type,
      int[] pixels, int width, int height) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {

    RepresentableFeatureGenerator generator = (
        RepresentableFeatureGenerator) Class.forName(generator_name).
        newInstance();

    FeatureRepresentable fr = generator.generateFeatureRepresentable(
        feature_type,
        pixels, width, height);

    return fr;
  }

  /**
   * Generate image features
   */
  public static class Generate {

    public final static String USAGE = "Usage: -i image_name -g generator [-f feature_type] [-h output_html_file]";

    public static void main(String[] args) {
      try {
        // init configuration
        Configuration.init(args);

        // get the image name
        String img_name = ReadArgu.getOption('i', args);
        if (img_name == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String image_path = Configuration.getParameter("IMAGE_PATH", "");

        // read in image
        Dimension d = new Dimension();
        int[] pixels = yi.util.io.ImageIOUtil.read(image_path + img_name, d);

        String feature_desc_str = ReadArgu.getOption('f', args);
        if (feature_desc_str == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        FeatureDescriptor feature_desc = FeatureDescriptor.parse(feature_desc_str)[0];

        // rescale the image if necessary
        if (feature_desc.scale != 0) {
          // for blobworld, it must rescaled to 192x128 or 128x192
          if (feature_desc.generator.indexOf("BlobworldSegmenter") >= 0) {
            pixels = Resampler.scale4Blobworld(pixels, d);
          } else {
            FeatureUtil dummy = new FeatureUtil();
            ResampledDescriptor resampled = Resampler.scaleDown(pixels, d,
                dummy.RESAMPLER, feature_desc.scale);
            if (resampled != null) {
              pixels = resampled.m_resampled;
              d = resampled.m_newDimension;
            }
          }
        }

        // process
        Configuration.setParameter("SOURCE_NAME", img_name);
        yi.util.ImageUtil.setPixels(pixels);
        yi.util.ImageUtil.setDimension(d);

        // RUN
        long startTime = System.currentTimeMillis();
        FeatureRepresentable fr = FeatureUtil.generate(feature_desc.generator,
            feature_desc.feature,
            pixels, d.width, d.height);
        long endTime = System.currentTimeMillis();
        System.out.println("running time: " + (endTime - startTime));

        if (fr == null) {
          System.err.println("No feature generated");
          System.exit(1);
        }

        // rescale the result if possible
        Dimension rst_dim = d;
        if (feature_desc.rstScale != 0 && fr instanceof FeatureScalable) {
          rst_dim = Resampler.getDestSize(d.width, d.height,
                                          feature_desc.rstScale,
                                          feature_desc.rstScale);
          ( (FeatureScalable) fr).scale(rst_dim.width, rst_dim.height);
        }

        String output_html_str = ReadArgu.getOption('h', args);
        writeFeature(img_name, rst_dim, feature_desc, fr, output_html_str, true);
      }
      catch (Exception e) {
        e.printStackTrace();
      }

      System.exit(0);
    }
  }

  /**
   * read the saved feature from disk
   * @param img_name
   * @param generator_name
   * @param feature_type
   * @param scale
   * @param rst_scale
   * @return
   * @throws java.io.IOException
   * @throws ClassNotFoundException
   */
  public static FeatureRepresentable readFeature(
      String img_name,
      FeatureDescriptor desc) throws java.io.IOException,
      ClassNotFoundException {

    String name = FeatureDescriptor.getFeatureOutputName(desc, img_name, true);

    // read from file
    FeatureRepresentable fr = (FeatureRepresentable) yi.util.io.input.DIU_Ser.
        input(name);

    return fr;
  }

  /**
   * read the saved feature from disk
   * @param img_name
   * @param generator_name
   * @param feature_type
   * @param scale
   * @param rst_scale
   * @return
   * @throws java.io.IOException
   * @throws ClassNotFoundException
   */
  public static FeatureRepresentationList readRepresentation(
      String img_name,
      RepresentationDescriptor desc) throws java.io.IOException,
      ClassNotFoundException {

    String name = RepresentationDescriptor.getRepresentationOutputName(desc, img_name, true);

    // read from file
    FeatureRepresentationList frl = (FeatureRepresentationList) yi.util.io.input.DIU_Ser.
        input(name);
    return frl;
  }

  /**
   * save the feature to disk
   * @return
   */
  public static String writeFeature(
      String img_name,
      FeatureDescriptor desc,
      FeatureRepresentable fr) throws java.io.IOException {

    String output_name = FeatureDescriptor.getFeatureOutputName(desc, img_name, true);

    // make the output directory
    yi.util.io.File.makeDirectory(output_name, true);

    // write to disk
    yi.util.io.output.DOU_Ser.output(output_name , fr);
    return output_name;
  }

  /**
   * save the feature to disk
   * @param image_name
   * @param rep_desc
   * @param frl
   * @param output_html_str whether to output html file which includes the links
   *        to the original image and representation demo files
   * @param output_image_demo whether to output representation demos
   * @return
   */
  public static String writeFeature(
      String image_name, Dimension rst_dim,
      FeatureDescriptor desc,
      FeatureRepresentable fr, String output_html_str, boolean output_image_demo)
      throws java.io.IOException {

    String image_path = Configuration.getParameter("IMAGE_PATH", "");

    PrintStream out = null;
    if (output_html_str != null) {
      String html_name = FeatureDescriptor.getFeatureIndexHtmlName(desc, output_html_str, true);
      yi.util.io.File.makeDirectory(html_name, true);

      out = new PrintStream(
          new BufferedOutputStream(new FileOutputStream(html_name,
          true)));

      out.println("<a href=" + '"' +
                  image_path + image_name + '"' +
                  ">"+
                  "<img src=" + '"' +
                  image_path + image_name + '"' +
                  " width=" + rst_dim.width +
                  " height=" + rst_dim.height +
                  "></a>");
    }

    // write to disk
    String file_name = FeatureUtil.writeFeature(image_name, desc, fr);

    System.out.println("feature saved to " + file_name);

    if (output_image_demo && fr instanceof ImageDemoCapable) {
      ImageDemoCapable idc = (ImageDemoCapable) fr;

      // pseudo-color demo
      Dimension pc_dim = new Dimension();
      int[] demo = idc.graphicDemo(pc_dim);
      if (demo != null) {
        String gdemo_name = FeatureDescriptor.getFeatureGraphicDemoName(desc, image_name, true);
        yi.util.io.File.makeDirectory(gdemo_name, true);
        OutputResult.outputGIFImage(gdemo_name, demo,
                                    pc_dim.width,
                                    pc_dim.height, false);
        if (out != null) {
          gdemo_name = FeatureDescriptor.getFeatureGraphicDemoName(desc, image_name, false);
          out.println("<img src=" + '"' +
                      gdemo_name
                      + '"' +
                      " width=" + pc_dim.width + " height=" +
                      pc_dim.height +
                      ">");
        }
      }

      // color demo
      demo = idc.colorDemo(pc_dim);
      if (demo != null) {
        String cdemo_name = FeatureDescriptor.getFeatureColorDemoName(desc, image_name, true);
        yi.util.io.File.makeDirectory(cdemo_name, true);
        OutputResult.outputGIFImage(cdemo_name, demo,
                                    pc_dim.width,
                                    pc_dim.height, false);
        if (out != null) {
          cdemo_name = FeatureDescriptor.getFeatureColorDemoName(desc, image_name, false);
          out.println("<img src=" + '"' +
                      cdemo_name
                      + '"' +
                      " width=" + pc_dim.width + " height=" +
                      pc_dim.height +
                      ">");
        }
      }
    }

    if (out != null) {
      out.println("<br>");
      out.close();
    }

    return file_name;

  }

  /**
   * save the representation to disk
   * @return
   */
  public static String writeRepresentation(
      String img_name,
      RepresentationDescriptor rep_desc,
      FeatureRepresentationList frl) throws java.io.IOException {

    String output_name = RepresentationDescriptor.getRepresentationOutputName(rep_desc, img_name, true);

    // make the output directory
    yi.util.io.File.makeDirectory(output_name, true);

    yi.util.io.output.DOU_Ser.output(output_name , frl);
    return output_name;
  }

  /**
   * save the representation to disk
   * @param image_name
   * @param rep_desc
   * @param frl
   * @param output_html_str whether to output html file which includes the links
   *        to the original image and representation demo files
   * @param output_image_demo whether to output representation demos
   * @return
   */
  public static String writeRepresentation(
      String image_name,
      RepresentationDescriptor rep_desc,
      FeatureRepresentationList frl, String output_html_str, boolean output_image_demo)
      throws java.io.IOException{

    String image_path = Configuration.getParameter("IMAGE_PATH", "");

    PrintStream out = null;
    if (output_html_str != null) {
      String html_name = RepresentationDescriptor.getRepresentationIndexHtmlName(rep_desc, output_html_str, true);
      yi.util.io.File.makeDirectory(html_name, true);

      out = new PrintStream(
          new BufferedOutputStream(new FileOutputStream(html_name,
          true)));

      if (frl instanceof RgnFeatureRepresentationList) {
        out.println("<a href=" + '"' +
                    image_path + image_name + '"' +
                    ">" +
                    "<img src=" + '"' +
                    image_path + image_name + '"' +
                    " width=" + ((RgnFeatureRepresentationList)frl).imgWidth +
                    " height=" + ((RgnFeatureRepresentationList)frl).imgHeight +
                    "></a>");
      } else {
        out.println("<a href=" + '"' +
                    image_path + image_name + '"' +
                    ">" +
                    "<img src=" + '"' +
                    image_path + image_name + '"' +
                    "></a>");
      }
    }

    // write to disk
    String file_name = FeatureUtil.writeRepresentation(
        image_name, rep_desc, frl);

    System.out.println("feature saved to " + file_name);

    if (output_image_demo && frl instanceof ImageDemoCapable) {
      ImageDemoCapable idc = (ImageDemoCapable) frl;

      // pseudo-color demo
      Dimension pc_dim = new Dimension();
      int[] demo = idc.graphicDemo(pc_dim);
      if (demo != null) {
        String gdemo_name = RepresentationDescriptor.getRepresentationGraphicDemoName(rep_desc, image_name, true);
        yi.util.io.File.makeDirectory(gdemo_name, true);
        OutputResult.outputGIFImage(gdemo_name, demo,
                                    pc_dim.width,
                                    pc_dim.height, false);
        if (out != null) {
          gdemo_name = RepresentationDescriptor.getRepresentationGraphicDemoName(rep_desc, image_name, false);
          out.println("<img src=" + '"' +
                      gdemo_name
                      + '"' +
                      " width=" + pc_dim.width + " height=" +
                      pc_dim.height +
                      ">");
        }
      }

      // color demo
      demo = idc.colorDemo(pc_dim);
      if (demo != null) {
        String cdemo_name = RepresentationDescriptor.getRepresentationColorDemoName(rep_desc, image_name, true);
        yi.util.io.File.makeDirectory(cdemo_name, true);
        OutputResult.outputGIFImage(cdemo_name, demo,
                                    pc_dim.width,
                                    pc_dim.height, false);
        if (out != null) {
          cdemo_name = RepresentationDescriptor.getRepresentationColorDemoName(rep_desc, image_name, false);
          out.println("<img src=" + '"' +
                      cdemo_name
                      + '"' +
                      " width=" + pc_dim.width + " height=" +
                      pc_dim.height +
                      ">");
        }
      }
    }

    if (out != null) {
      out.println("<br>");
      out.close();
    }

    return file_name;

  }

//  /**
//   * Generate image features
//   */
//  public static class Read {
//
//    public final static String USAGE = "Usage: -g generator -f feature_type -i image_name -p path -s scale -r result_scale";
//
//    public static void main(String[] args) {
//      try {
//        // init configuration
//        Configuration.init(args);
//
//        // get the image name
//        String img_name = ReadArgu.getOption('i', args);
//        if (img_name == null) {
//          System.err.println(USAGE);
//          System.exit(1);
//        }
//
//        String from_path = ReadArgu.getOption('p', args, "");
//
//        // get the scale
//        String scale_str = ReadArgu.getOption('s', args, "0");
//        int scale = scale = Integer.parseInt(scale_str);
//
//        String generator_name = ReadArgu.getOption('g', args);
//        if (generator_name == null) {
//          System.err.println(USAGE);
//          System.exit(1);
//        }
//
//        // get the feature type name
//        String feature_type = ReadArgu.getOption('f', args,
//                                                 "FeatureRepresentable");
//
//        // get the result scale
//        String rst_scale_str = ReadArgu.getOption('r', args, "0");
//        int rst_scale = Integer.parseInt(rst_scale_str);
//
//        FeatureRepresentable fr = readFeature(
//            from_path + img_name, generator_name, feature_type, scale,
//            rst_scale);
//
//        System.out.println(fr);
//      }
//      catch (Exception e) {
//        e.printStackTrace();
//      }
//
//      System.exit(0);
//    }
//  }

  public static FeatureRepresentationList represent(
      FeatureRepresentable fr, RepresentationDescriptor desc) throws Exception{

    String generator_name = desc.generator;
    String represent_type = desc.representation;
    int mode = desc.mode;

    FeatureRepresentationList data = null;
    if (represent_type.indexOf("PolygonFeatureRepresentationList") >= 0) {
      if (fr instanceof PolygonFeatureRepresentable) {
        data = ( (PolygonFeatureRepresentable) fr).
            representPolygonFeatures(generator_name, mode);
      }
      else {
        System.err.println(fr.getClass().getName() +
                           " is not PolygonFeatureRepresentable");
      }
    }
    else if (represent_type.indexOf("LabeledRgnFtRepList") >= 0) {
      if (fr instanceof LabeledRgnFtRepresentable) {
        data = ( (LabeledRgnFtRepresentable) fr).
            representLabeledRgnFts(generator_name, mode);
      }
      else {
        System.err.println(fr.getClass().getName() +
                           " is not LabeledRgnFtRepresentable");
      }
    }
    else if (represent_type.indexOf("EllipseFeatureRepresentationList") >= 0) {
      if (fr instanceof EllipseFeatureRepresentable) {
        data = ( (EllipseFeatureRepresentable) fr).
            representEllipseFeatures(generator_name, mode);

      }
      else {
        System.err.println(fr.getClass().getName() +
                           " is not EllipseFeatureRepresentable");
      }
    }

    else {
      data = fr.represent(generator_name, mode);
    }
    return data;
  }

  /**
   * Generate image features
   */
  public static class Represent {

    public final static String USAGE = "Usage: -i image_name -f feature_descriptor [-r represent_descriptor] [-h output_html_file]";

    public static void main(String[] args) {
      try {
        // init configuration
        Configuration.init(args);

        // get the image name
        String img_name = ReadArgu.getOption('i', args);
        if (img_name == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String image_path = Configuration.getParameter("IMAGE_PATH", "");

        String feature_desc_str = ReadArgu.getOption('f', args);
        if (feature_desc_str == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        FeatureDescriptor feature_desc = FeatureDescriptor.parse(feature_desc_str)[0];

        FeatureRepresentable fr = readFeature(img_name, feature_desc);

        // represent
        String represent_desc_str = ReadArgu.getOption('r', args);
        RepresentationDescriptor rep_desc = null;
        if (represent_desc_str == null) {
          rep_desc = RepresentationDescriptor.getDefaultRepresentationDescriptor(feature_desc.generator);
        } else {
          rep_desc = RepresentationDescriptor.parse(represent_desc_str)[0];
        }

        FeatureRepresentationList frl = represent(fr, rep_desc);

        if (frl == null) {
          System.err.println("No representation generated");
          System.exit(1);
        }

        System.out.println("feature dim = " + frl.featureDimension + " x " + frl.size());
        OutputResult.print(System.out, frl.as2DDoubleArray());

        String output_html = ReadArgu.getOption('h', args);
        writeRepresentation(img_name, rep_desc, frl, output_html, true);
      }
      catch (Exception e) {
        e.printStackTrace();
      }

      System.exit(0);
    }
  }

  /**
   * put the color regions from a list of images together
   */
  public static class GatherColors {

    public final static String USAGE = "Usage: -i image_list_file -f feature_descriptor [-r represent_descriptor] [-h output_html_file]";

    public static void main(String[] args) {
      try {
        // init configuration
        Configuration.init(args);

        // get the image name
        String img_list_name = ReadArgu.getOption('i', args);
        if (img_list_name == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String image_path = Configuration.getParameter("IMAGE_PATH", "");

        String feature_desc_str = ReadArgu.getOption('f', args);
        if (feature_desc_str == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        FeatureDescriptor feature_desc = FeatureDescriptor.parse(feature_desc_str)[0];

        Plot3DPanel pp = new Plot3DPanel();
        String[] img_names = yi.util.io.ReadArgu.readStrings(img_list_name);
        LinkedList color_list = new LinkedList();
        LinkedList lab_color_list = new LinkedList();
        for (int i=0; i<img_names.length; i++) {
          String img_name = img_names[i];
          FeatureRepresentable fr = readFeature(img_name, feature_desc);

          // represent
          String represent_desc_str = ReadArgu.getOption('r', args);
          RepresentationDescriptor rep_desc = null;
          if (represent_desc_str == null) {
            rep_desc = RepresentationDescriptor.
                getDefaultRepresentationDescriptor(feature_desc.generator);
          }
          else {
            rep_desc = RepresentationDescriptor.parse(represent_desc_str)[0];
          }

          FeatureRepresentationList frl = represent(fr, rep_desc);

          if (frl == null) {
            System.err.println("No representation generated");
            System.exit(1);
          }

          System.out.println("feature dim = " + frl.featureDimension + " x " +
                             frl.size());
          OutputResult.print(System.out, frl.as2DDoubleArray());

//          String output_html = ReadArgu.getOption('h', args);
//          writeRepresentation(img_name, rep_desc, frl, output_html, true);

          double[][] labs = frl.as2DDoubleArray();
          for (int j = 0; j < labs.length; j++) {
            lab_color_list.add(labs[j]);
            int color = yi.iu.processor.color.CIELab.lab2rgb(labs[j]);
            color_list.add(new Integer(color));
            int[] rgb = yi.iu.processor.color.ColorUtil.RGB2IntArray(color);
            AbstractMatrix A = new Matrix(1, 3);
            A.set(0, 0, rgb[0]);
            A.set(0, 1, rgb[1]);
            A.set(0, 2, rgb[2]);
            pp.addPlot(A, Integer.toString(i), "SCATTER",
                       new Color(rgb[0], rgb[1], rgb[2]));
          }
        }
        pp.setAxesLabels("R", "G", "B");
        pp.setPreferredSize(new java.awt.Dimension(600, 600));
        org.jmat.gui.FrameView viewer = new org.jmat.gui.FrameView("RGB Color Space", pp);

        pp.setFixedBounds(0, 0, 255);
        pp.setFixedBounds(1, 0, 255);
        pp.setFixedBounds(2, 0, 255);

        pp.invalidate();

        yi.util.io.output.DOU_Ser.output(img_list_name + ".color.list", color_list);
        yi.util.io.output.DOU_Ser.output(img_list_name + ".lab.list", lab_color_list);

      }
      catch (Exception e) {
        e.printStackTrace();
      }

//      System.exit(0);
    }
  }

 /*
  * Draw the color list in 3D color space
  */
 public static class PlotColors {

   public final static String USAGE = "Usage: -i color_list_file";

   public static void main(String[] args) {
     try {
       // init configuration
       Configuration.init(args);

       // get the image name
       String color_list_name = ReadArgu.getOption('i', args);
       if (color_list_name == null) {
         System.err.println(USAGE);
         System.exit(1);
       }

       LinkedList color_list = (LinkedList)yi.util.io.input.DIU_Ser.input(color_list_name);
       int[] colors  = yi.util.data.Conversion.toIntArray(color_list);

       Plot3DPanel pp = yi.iu.processor.color.ColorUtil.drawPointsInRGBSpace(colors);
     }
     catch (Exception e) {
       e.printStackTrace();
     }

//      System.exit(0);
   }
 }

 /*
  * Draw the color list in 3D color space
  */
 public static class PlotCIELabColors {

   public final static String USAGE = "Usage: -i color_list_file";

   public static void main(String[] args) {
     try {
       // init configuration
       Configuration.init(args);

       // get the image name
       String color_list_name = ReadArgu.getOption('i', args);
       if (color_list_name == null) {
         System.err.println(USAGE);
         System.exit(1);
       }

       LinkedList color_list = (LinkedList)yi.util.io.input.DIU_Ser.input(color_list_name);
       double[][] colors  = yi.util.data.Conversion.to2DDoubleArray(color_list);

       Plot3DPanel pp = yi.iu.processor.color.ColorUtil.drawPointsInLabSpace(colors);
     }
     catch (Exception e) {
       e.printStackTrace();
     }

//      System.exit(0);
   }
 }
}