package netmatch.algorithm;

import cytoscape.Cytoscape;

import javax.swing.*;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;

public class myMatch {
  private JPanel frame;
  private Graph g1;
  private Graph g2;
  private netMatch instance;
  //private static int k;

  public myMatch(netMatch instance, JPanel frame) {
    this.frame = frame;
    this.instance = instance;
    g1 = null;
    g2 = null;
    //k = 0;
  }

  /** Finds a matching between two graph, if it exists, given the
   initial state of the matching process. Returns true a match has been found.
   pn is assigned the number of matched nodes, and  c1 and c2 will
   contain the ids of the corresponding nodes in the two graphs. */
  /*public boolean match(VF2MonoState s,myInteger pn,int c1[],int c2[]) {
    return match(pn,c1,c2,s);
  }*/

  /**
   * Visits all the matches between two graphs, given the initial state
   * of the match. Returns the number of visited matches.
   * Stops when there are no more matches, or the visitor vis returns true.
   */
  public myInteger match(VF2MonoState s, BufferedWriter out) {
    g1 = s.GetGraph1();
    g2 = s.GetGraph2();

    // Choose a conservative dimension for the arrays
    int n;
    if(g1.NodeCount() < g2.NodeCount())
      n = g2.NodeCount();
    else
      n = g1.NodeCount();
    int[] c1 = new int[n];
    int[] c2 = new int[n];
    myInteger count = new myInteger(0);
    match(c1, c2, out, s, count);
    return count;
  }

  public myInteger match(VF2MonoState s, ArrayList out, ArrayList source) {
    g1 = s.GetGraph1();
    g2 = s.GetGraph2();

    // Choose a conservative dimension for the arrays
    int n;
    if(g1.NodeCount() < g2.NodeCount())
      n = g2.NodeCount();
    else
      n = g1.NodeCount();
    int[] c1 = new int[n];
    int[] c2 = new int[n];
    myInteger count = new myInteger(0);
    match(c1, c2, out, source, s, count);
    return count;
  }

  /** Finds a matching between two graphs, if it exists, starting
   from state s. Returns true a match has been found.
   pn is assigned the number of matched nodes, and c1 and c2
   will contain the ids of the corresponding nodes in the two graphs. */
  /*private boolean match(myInteger pn,int c1[],int c2[],VF2MonoState s) {
    if(s.IsGoal()) {
      pn.setValue(s.CoreLen());
      s.GetCoreSet(c1,c2);
      return true;
    }
    if(s.IsDead())
      return false;
    myInteger n1 = new myInteger(Common.NULL_NODE);
    myInteger n2 = new myInteger(Common.NULL_NODE);
    boolean found = false;
    try {
      while(!found && s.NextPair(n1,n2,n1.intValue(),n2.intValue())) {
        if(s.IsFeasiblePair(n1.intValue(),n2.intValue())) {
          VF2MonoState s1 = s.Clone();
          s1.AddPair(n1.intValue(),n2.intValue());
          found = match(pn,c1,c2,s1);
          s1.BackTrack();
          s1 = null;
        }
      }
    }
    catch(Exception e) {
      System.err.println("Exception in Match: "+e.toString());
      System.exit(1);
    }
    return found;
  }*/

  /**
   * boolean match(c1, c2, vis, usr_data, pcount)
   * Visits all the matchings between two graphs, starting from state s.
   * Returns true if the caller must stop the visit.
   * Stops when there are no more matches, or the visitor vis returns true.
   */
  private boolean match(int c1[], int c2[], BufferedWriter out, VF2MonoState s, myInteger pcount) {
    if(s.IsGoal()) {
      pcount.setValue(pcount.intValue() + 1);
      int n = s.CoreLen();
      s.GetCoreSet(c1, c2);
      return vis(n, c1, c2, out);
    }
    if(s.IsDead())
      return false;
    myInteger n1 = new myInteger(Common.NULL_NODE);
    myInteger n2 = new myInteger(Common.NULL_NODE);
    try {
      while(s.NextPair(n1, n2, n1.intValue(), n2.intValue())) {
        if(s.IsFeasiblePair(n1.intValue(), n2.intValue())) {
          VF2MonoState s1 = s.Clone();
          s1.AddPair(n1.intValue(), n2.intValue());
          if(match(c1, c2, out, s1, pcount)) {
            s1.BackTrack();
            s1 = null;
            return true;
          }
          else {
            s1.BackTrack();
            s1 = null;
          }
        }
      }
    }
    catch(Exception e) {
      if(frame != null)
        JOptionPane.showMessageDialog(Cytoscape.getDesktop(), "Error in Matching!", "NetMatch Error", JOptionPane.ERROR_MESSAGE);
      else
        System.err.println("NetMatch Error in Matching!");
      return false;
    }
    return false;
  }

  private boolean match(int c1[], int c2[], ArrayList out, ArrayList source, VF2MonoState s, myInteger pcount) {
    if(s.IsGoal()) {
      pcount.setValue(pcount.intValue() + 1);
      int n = s.CoreLen();
      s.GetCoreSet(c1, c2);
      return vis(n, c1, c2, out, source);
    }
    if(s.IsDead())
      return false;
    myInteger n1 = new myInteger(Common.NULL_NODE);
    myInteger n2 = new myInteger(Common.NULL_NODE);
    try {
      while(s.NextPair(n1, n2, n1.intValue(), n2.intValue())) {
        if(instance.checkTask())
          return false;
        if(s.IsFeasiblePair(n1.intValue(), n2.intValue())) {
          VF2MonoState s1 = s.Clone();
          s1.AddPair(n1.intValue(), n2.intValue());
          if(match(c1, c2, out, source, s1, pcount)) {
            s1.BackTrack();
            s1 = null;
            return true;
          }
          else {
            s1.BackTrack();
            s1 = null;
          }
        }
      }
    }
    catch(Exception e) {
      e.printStackTrace();
      if(frame != null)
        JOptionPane.showMessageDialog(Cytoscape.getDesktop(), "Error in Matching! Check network and query attributes!", "NetMatch Error", JOptionPane.ERROR_MESSAGE);
      else
        System.err.println("NetMatch Error in Matching!");
      return false;
    }
    return false;
  }

  /* Prints the matched pairs on the file */
  private boolean vis(int n, int ni1[], int ni2[], BufferedWriter out) {
    try {
      System.out.println();
      for(int i = 0;i < n;i++) {
        System.out.print("(" + ni1[i] + ", " + ni2[i] + ')');
        out.write("(" + ni1[i] + ", " + ni2[i] + ')');
      }
      System.out.println();
      out.write("\r\n");
    }
    catch(IOException e) {
      System.err.println("IO Exception in myMatch: " + e.toString());
    }
    return false;// Return false to search for the next matching
  }

  private boolean vis(int n, int ni1[], int ni2[], ArrayList array, ArrayList source) {
    try {
      int tmp1[] = new int[n];
      int tmp2[] = new int[n];
      for(int i = 0;i < n;i++) {
        tmp1[i] = g1.getCyNetworkID(ni1[i]);
        tmp2[i] = g2.getCyNetworkID(ni2[i]);// Translate Node ID in CyNetwork ID
      }
      array.add(tmp2);
      source.add(tmp1);
    }
    catch(Exception e) {
      if(frame != null)
        JOptionPane.showMessageDialog(Cytoscape.getDesktop(), "NetMatch Error. Unable to write matching!", "NetMatch Error", JOptionPane.ERROR_MESSAGE);
      else
        System.err.println("NetMatch Error. Unable to write Mathcing!");

    }
    return false;// Return false to search for the next matching
  }
}