import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;

//-------------------------------------
class Henker extends JFrame {

static Henker mf;

CDA     cnvs;
JButton sbar;

//-------------------------------------
ArrayList<Character> available, guess;
ArrayList<String>    defs, wrds;

boolean gamo;
Font    uif;
int     cnvx, cnvy, maxx, maxy, winx, winy, wxp, wyp;
int     btnh, btnw, uifs;
int     err, wrdidx, xn, yn;
Random  rndm;
String  stxt, uifn;
String  alphabet, target;

//-------------------------------------
static final int UIFS   = 64;
static final int WXP    = 50;
static final int WYP    = 80;
static final String UIFN = "Sans";
static final String UIFNLIT = "UIFN";
static final String UIFSLIT = "UIFS";
static final String WXPLIT = "WXP";
static final String WYPLIT = "WYP";

//-------------------------------------
class ALookup implements ActionListener {
public void actionPerformed(ActionEvent qae) {
String  s;
  s = Util.GetStringValue("word", "", mf);
  if (s.isEmpty()) return;
  LookupWord(s);
}}

//-------------------------------------
class ALookupDef implements ActionListener {
public void actionPerformed(ActionEvent qae) {
String  s;
  if (wrdidx==-1) return;
  s = Util.GetStringValue("word", defs.get(wrdidx), mf);
  if (s.isEmpty()) return;
  LookupWord(s);
}}

//-------------------------------------
class AMinimize implements ActionListener {
public void actionPerformed(ActionEvent qae) {
  mf.setState(Frame.ICONIFIED);
}}

//-------------------------------------
class ANextWord implements ActionListener {
public void actionPerformed(ActionEvent qae) {
  NextWord();
}}

//-------------------------------------
public void AppQuit() {
  System.exit(0);
}

//-------------------------------------
class AQuit implements ActionListener {
public void actionPerformed(ActionEvent ae) {
  AppQuit();
}}

//-------------------------------------
class CBButton implements ActionListener {
public void actionPerformed(ActionEvent ae) {
  PopupMain();
}}

//-------------------------------------
class CBKey extends KeyAdapter {
public void keyPressed(KeyEvent ke) {
  if (ke.getKeyCode() == KeyEvent.VK_F1) PopupMain();
  if (ke.getKeyCode() == KeyEvent.VK_A) CheckGuess('A');
  if (ke.getKeyCode() == KeyEvent.VK_B) CheckGuess('B');
  if (ke.getKeyCode() == KeyEvent.VK_C) CheckGuess('C');
  if (ke.getKeyCode() == KeyEvent.VK_D) CheckGuess('D');
  if (ke.getKeyCode() == KeyEvent.VK_E) CheckGuess('E');
  if (ke.getKeyCode() == KeyEvent.VK_F) CheckGuess('F');
  if (ke.getKeyCode() == KeyEvent.VK_G) CheckGuess('G');
  if (ke.getKeyCode() == KeyEvent.VK_H) CheckGuess('H');
  if (ke.getKeyCode() == KeyEvent.VK_I) CheckGuess('I');
  if (ke.getKeyCode() == KeyEvent.VK_J) CheckGuess('J');
  if (ke.getKeyCode() == KeyEvent.VK_K) CheckGuess('K');
  if (ke.getKeyCode() == KeyEvent.VK_L) CheckGuess('L');
  if (ke.getKeyCode() == KeyEvent.VK_M) CheckGuess('M');
  if (ke.getKeyCode() == KeyEvent.VK_N) CheckGuess('N');
  if (ke.getKeyCode() == KeyEvent.VK_O) CheckGuess('O');
  if (ke.getKeyCode() == KeyEvent.VK_P) CheckGuess('P');
  if (ke.getKeyCode() == KeyEvent.VK_Q) CheckGuess('Q');
  if (ke.getKeyCode() == KeyEvent.VK_R) CheckGuess('R');
  if (ke.getKeyCode() == KeyEvent.VK_S) CheckGuess('S');
  if (ke.getKeyCode() == KeyEvent.VK_T) CheckGuess('T');
  if (ke.getKeyCode() == KeyEvent.VK_U) CheckGuess('U');
  if (ke.getKeyCode() == KeyEvent.VK_V) CheckGuess('V');
  if (ke.getKeyCode() == KeyEvent.VK_W) CheckGuess('W');
  if (ke.getKeyCode() == KeyEvent.VK_X) CheckGuess('X');
  if (ke.getKeyCode() == KeyEvent.VK_Y) CheckGuess('Y');
  if (ke.getKeyCode() == KeyEvent.VK_Z) CheckGuess('Z');
  RenderGame();
}}

//-------------------------------------
class CBMouse extends MouseAdapter {
//-----------------
public void mouseClicked(MouseEvent me) {
Character c;
int       idx, x, xi, y, yi;
  x = me.getX();
  y = me.getY();
  xi = x/btnw;
  yi = y/btnh;
  idx = xi+(yi*xn);
  if (idx < available.size()) {
    c = available.get(idx);
    if (!c.equals(' ')) CheckGuess(c);
  }
  RenderGame();
}
//-----------------
public void mouseDragged(MouseEvent me) {
int x, y;
  x = me.getX();
  y = me.getY();
}
//-----------------
public void mousePressed(MouseEvent me) {
int x, y;
  x = me.getX();
  y = me.getY();
}
//-----------------
public void mouseReleased(MouseEvent me) {
int x, y;
  x = me.getX();
  y = me.getY();
}
}

//-------------------------------------
class CBResize extends ComponentAdapter {
public void componentResized(ComponentEvent ce) {
  cnvy = cnvs.getHeight();
  cnvx = cnvs.getWidth();
}}

//---------------------------------------------
void CheckGuess(Character ac) {
boolean   fnd;
Character cc, tc;
int       i;
  fnd = false;
  cc = Character.toUpperCase(ac);
  for (i=0; i<target.length(); i++) {
    tc = Character.toUpperCase(target.charAt(i));
    if (cc==tc) {
      guess.set(i, cc);
      fnd = true;
    }
  }
  for (i=0; i<available.size(); i++) {
    tc = Character.toUpperCase(available.get(i));
    if (cc==tc) available.set(i, ' ');
  }
  if (!fnd) {
    err++;
  }
}

//-------------------------------------
public boolean GameOver() {
boolean solved;
int     i, numl;
  solved = true;
  numl = target.length();
  for (i=0; i<numl; i++) if (guess.get(i)=='?') solved = false;
  return(solved);
}

//-------------------------------------
public Henker(String[] args) {
Dimension dim;
Toolkit   tk;
  tk = Toolkit.getDefaultToolkit();
  dim = tk.getScreenSize();
  maxx = dim.width;
  maxy = dim.height;
}

//-------------------------------------
public void Initlz() {
  InitlzData();
  ReadIniFile();
  InitlzUI();
  LayoutUI();
}

//-------------------------------------
public void InitlzData() {
InputStream is;
  uifs = UIFS;
  uifn = UIFN;
  wxp = WXP;
  wyp = WYP;
  wrdidx = -1;
  stxt = "Click Here";
  rndm = new Random();
  defs = new ArrayList<String>();
  wrds = new ArrayList<String>();
  is = mf.getClass().getResourceAsStream("/ed.txt");
  ReadLinesIntoAL(is, defs);
  is = mf.getClass().getResourceAsStream("/ew.txt");
  ReadLinesIntoAL(is, wrds);
  available = new ArrayList<Character>();
  guess = new ArrayList<Character>();
  alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
}

//-------------------------------------
public void InitlzUI() {
Font f;
  winx = (maxx*wxp)/100;
  winy = (maxy*wyp)/100;
  uif = new Font(uifn, Font.BOLD, uifs);
  f = new Font(uifn, Font.BOLD, uifs/2);
  Util.PutUIManFonts(f);
  cnvs = new CDA();
  cnvs.setBackground(new Color(255, 255, 255));
  cnvs.addKeyListener(new CBKey());
  cnvs.addComponentListener(new CBResize());
  cnvs.addMouseListener(new CBMouse());
  cnvs.addMouseMotionListener(new CBMouse());
  sbar = new JButton(stxt);
  sbar.setFont(uif);
  sbar.addActionListener(new CBButton());
  sbar.setBackground(new Color(16, 16, 128));
  sbar.setForeground(new Color(255, 255, 0));
}

//-------------------------------------
public void LayoutUI() {
Image     iimg;
Toolkit   tk;
URL       iurl;
  mf.addKeyListener(new CBKey());
  mf.setSize(winx, winy);
  mf.setLocation((maxx-winx)/2, (maxy-winy)/2);
  mf.getContentPane().add(sbar, BorderLayout.NORTH);
  mf.getContentPane().add(cnvs, BorderLayout.CENTER);
  mf.setTitle("Henker");
  mf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  mf.setVisible(true);
  iurl = this.getClass().getResource("/Henker.png");
  tk = Toolkit.getDefaultToolkit();
  iimg = tk.createImage(iurl);
  mf.setIconImage(iimg);
}

//-------------------------------------
public void LookupWord(String wrd) {
int     pos;
String  lwrd, msg;
  lwrd = wrd.toUpperCase();
  pos  = wrds.indexOf(lwrd);
  if (pos!=-1) {
    msg = lwrd+"\n"+defs.get(pos);
    Util.DspMsg(msg, mf);
  }
}

//-------------------------------------
public static void main(String[] args) {
  System.out.println("starting Henker\n");
  mf = new Henker(args);
  mf.Initlz();
}

//-------------------------------------
void NextWord() {
ArrayList<Character> cpya;
Character            cc;
int                  i, ne, numl, numw;
String               s;
  numw = wrds.size();
  i = rndm.nextInt(numw);
  target = wrds.get(i);
  wrdidx = i;
  guess.clear();
  numl = target.length();
  for (i=0; i<numl; i++) guess.add('?');
  available.clear();
  for (i=0; i<26; i++) available.add(alphabet.charAt(i));
  CheckGuess('A');
  CheckGuess('E');
  CheckGuess('I');
  CheckGuess('O');
  CheckGuess('U');
  CheckGuess('Y');
  ne = available.size();
  cpya = new ArrayList<Character>();
  for (i=0; i<ne; i++) {
    cc = available.get(i);
    if (cc!=' ') cpya.add(cc);
  }
  ne = cpya.size();
  available.clear();
  for (i=0; i<ne; i++) available.add(cpya.get(i)); 
  err = 0;
  gamo = false;
  RenderGame();
}

//-------------------------------------
public void PopupMain() {
JMenuItem     mi;
JPopupMenu    pum;
  pum = new JPopupMenu();
  mi = new JMenuItem("NextWord");
  mi.setMnemonic('N');
  mi.addActionListener(new ANextWord());
  mi.setFont(uif);
  pum.add(mi);
  mi = new JMenuItem("Lookup");
  mi.setMnemonic('L');
  mi.addActionListener(new ALookup());
  mi.setFont(uif);
  pum.add(mi);
  mi = new JMenuItem("LookupFromDefinition");
  mi.setMnemonic('D');
  mi.addActionListener(new ALookupDef());
  mi.setFont(uif);
  pum.add(mi);
  mi = new JMenuItem("Minimize");
  mi.setMnemonic('M');
  mi.addActionListener(new AMinimize());
  mi.setFont(uif);
  pum.add(mi);
  mi = new JMenuItem("Quit");
  mi.setMnemonic('Q');
  mi.addActionListener(new AQuit());
  mi.setFont(uif);
  pum.add(mi);
  pum.show(sbar, 0, 0);
}

//-------------------------------------
public void ReadIniFile() {
BufferedReader  br;
Charset         cs;
File            inif;
Path            ifp;
String          jini, line, nam, val;
String[]        sa;
  jini = System.getenv("JINI")+"Henker.ini";
  inif = new File(jini);
  if (!inif.exists()) {
    stxt = "no ini file";
    return;
  }
  ifp = inif.toPath();
  cs = Charset.forName("US-ASCII");
  try {
    br = Files.newBufferedReader(ifp, cs);
    while ((line = br.readLine()) != null) {
      sa = line.split("=");
      if (sa.length==2) {
        nam = sa[0];
        val = sa[1];
        if (nam.compareTo(UIFNLIT)==0) uifn = val;
        if (nam.compareTo(UIFSLIT)==0) uifs = Integer.parseInt(val);
        if (nam.compareTo(WXPLIT)==0) wxp = Integer.parseInt(val);
        if (nam.compareTo(WYPLIT)==0) wyp = Integer.parseInt(val);
      }
    }
  } catch (Exception x) { System.err.format("Exception: %s%n", x); }
}

//-------------------------------------
public void ReadLinesIntoAL(InputStream is, ArrayList<String> sa) {
BufferedReader    br;
InputStreamReader isr;
String            line;
  try {
    isr = new InputStreamReader(is);
    br = new BufferedReader(isr);
    do {
      line = br.readLine();
      if (line != null) sa.add(line);
    } while (line != null);         
  } catch (Exception x) { System.err.format("Exception: %s%n", x); }
}

//-------------------------------------
public void RenderAvailable(Graphics g) {
Character c;
int       i, j;
int       idx, x, y;
Rectangle br;
  g.setFont(uif);
  g.setColor(new Color(32, 128, 32));
  xn = 5;
  yn = 4;
  btnw = cnvx/xn;
  btnh = cnvy/(yn*2);
  for (i=0; i<yn; i++) {
    for (j=0; j<xn; j++) {
       idx = j+(i*xn);
       c = available.get(idx);
       br = new Rectangle(btnw*j, btnh*i, btnw, btnh);
       RenderCharCentered(g, c, br);
    }
  }
}

//-------------------------------------
void RenderCharCentered(Graphics g, char c, Rectangle r) {
FontMetrics fm;
int         x, y;
String      s;
  fm = g.getFontMetrics();
  x = r.x+(r.width-fm.charWidth(c))/2;
  y = r.y+((r.height-fm.getHeight())/2)+fm.getAscent();
  s = "";
  s += c;
  g.drawString(s, x, y);
}

//-------------------------------------
public void RenderGame() {
BufferedImage img;
Font          f;
Graphics      g;
Graphics2D    g2;
Rectangle     r;
  img = new BufferedImage(cnvx, cnvy, BufferedImage.TYPE_INT_RGB);
  g = img.getGraphics();  
  g2 = (Graphics2D)g;
  g2.setStroke(new BasicStroke(4));
  g.setColor(new Color(255, 255, 255));
  g.fillRect(0, 0, cnvx, cnvy);
  RenderAvailable(g);
  RenderGuess(g);
  RenderStatus();
  g.dispose();
  cnvs.img = img;
  cnvs.repaint();
  cnvs.requestFocusInWindow();
  if (GameOver()&&!gamo) {
    gamo = true;
    f = new Font(uifn, Font.BOLD, uifs/2);
    Util.DspMsg(defs.get(wrdidx), f, mf);
  }
}

//-------------------------------------
public void RenderGuess(Graphics g) {
int           i, ne;
Rectangle     br;
StringBuilder sb;
  g.setFont(uif);
  g.setColor(new Color(32, 32, 128));
  br = new Rectangle(0, cnvy/2, cnvx, cnvy/2);
  ne = guess.size();
  sb = new StringBuilder(ne);
  for (i=0; i<ne; i++) sb.append(guess.get(i));
  RenderTextCentered(g, sb.toString(), br);
}

//-------------------------------------
void RenderStatus() {
  sbar.setText(String.format("#Errors %d", err));
}

//-------------------------------------
void RenderTextCentered(Graphics g, String s, Rectangle r) {
FontMetrics fm;
int         x, y;
  fm = g.getFontMetrics();
  x = r.x+(r.width-fm.stringWidth(s))/2;
  y = r.y+((r.height-fm.getHeight())/2)+fm.getAscent();
  g.drawString(s, x, y);
}

//-------------------------------------
}
