//
// Mini Java Editor
//
// By Lim Thye Chean
//

import iss.awt.*;
import iss.util.*;

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

class MJE extends Frame implements MJELabels {
  String path = null;
  String prefPath = "";
  String clipboard = "";
  String directory = null;
  boolean compileError = false;
  Table project = new Table();
  Hash windows = new Hash();

  MJEFiles list = new MJEFiles(this);
  MJEConsole console = new MJEConsole(this);

// Project options

  String main = "";
  String html = "";
  String cpath = "";
  String odir = "";
  boolean optimize = false;
  boolean debug = false;

// Preferences

  Font font;
  int width, height;
  String browser = "";
  String fonts[];
  String styles[] = {"Plain", "Bold", "Italic", "Bold-Italic"};

// Menu items

  Menu editMenu = new Menu("Edit");
  Menu projectMenu = new Menu("Project");
  MenuItem saveItem = new MenuItem(SAVE_PROJECT);
  MenuItem saveAsItem = new MenuItem(SAVE_PROJECT_AS);
  MenuItem closeItem = new MenuItem(CLOSE_PROJECT);

// Dialogs

  PreferencesDialog prefDialog = null;
  GlobalSearchDialog gsearchDialog = null;
  OptionsDialog optionsDialog = null;
  InfoDialog aboutDialog = null;

// Main

  public static void main(String args[]) {;
    MJE ed = new MJE("Mini Java Editor");

    if (args.length > 0) {
      ed.setTitle(new File(args[0]).getName());

      ed.path = args[0];
      ed.open(ed.path);
      ed.saveItem.enable();
    }
  }

  MJE(String title) {

  // Initialize

    fonts = getToolkit().getFontList();
    MenuBar menuBar = new MenuBar();

  // Create file menu

    Menu fileMenu = new Menu("File");
    fileMenu.add(NEW_FILE);
    fileMenu.add(OPEN_FILE);
    fileMenu.add("-");
    fileMenu.add(NEW_PROJECT);
    fileMenu.add(OPEN_PROJECT);
    fileMenu.add(saveItem);
    fileMenu.add(saveAsItem);
    fileMenu.add(closeItem);
    fileMenu.add("-");
    fileMenu.add(PREFERENCES);
    fileMenu.add("-");
    fileMenu.add(EXIT);
    menuBar.add(fileMenu);

  // Create edit menu

    editMenu.add(ADD_FILE);
    editMenu.add(ADD_NEW_FILE);
    editMenu.add(ADD_ALL_FILES);
    editMenu.add(ADD_ALL_JAVA_FILES);
    editMenu.add("-");
    editMenu.add(REMOVE_FILE);
    editMenu.add(REMOVE_ALL_FILES);
    editMenu.add("-");
    editMenu.add(MOVE_FILE_UP);
    editMenu.add(MOVE_FILE_DOWN);
    editMenu.add(SORT_FILES);
    menuBar.add(editMenu);

  // Add project menu
 
    projectMenu.add(COMPILE);
    projectMenu.add(BUILD);
    projectMenu.add(BUILD_ALL);
    projectMenu.add("-");
    projectMenu.add(RUN);
    projectMenu.add(RUN_APPLETVIEWER);
    projectMenu.add(RUN_WEB_BROWSER);
    projectMenu.add("-");
    projectMenu.add(GLOBAL_SEARCH);
    projectMenu.add(OPTIONS);
    menuBar.add(projectMenu);

  // Create help menu

    Menu helpMenu = new Menu("Help");
    helpMenu.add(ABOUT);
    menuBar.add(helpMenu);
    menuBar.setHelpMenu(helpMenu);

  // Get preferences

    String prefPath = System.getProperty("user.home") + 
      System.getProperty("file.separator") + ".mje";

    try {
      FileInputStream fs = new FileInputStream(prefPath);
      BufferedInputStream bs = new BufferedInputStream(fs);
      DataInputStream ds = new DataInputStream(bs);

    // Read font

      font = new Font(ds.readLine(), Integer.parseInt(ds.readLine()),
	Integer.parseInt(ds.readLine()));

    // Read window size

      width = Integer.parseInt(ds.readLine());
      height = Integer.parseInt(ds.readLine());

    // Read web browser path

      browser = ds.readLine();
    }
    catch(Exception err) {
      font = new Font("Courier", Font.PLAIN, 12);
      width = 80;
      height = 30;
    }

  // Show editor

    setMenuBar(menuBar);
    editMenu.disable();
    projectMenu.disable();
    saveItem.disable();
    saveAsItem.disable();
    setTitle(title);
    setBackground(Color.lightGray);

    add("West", list);
    add("Center", console);
    resize(700, 250);
    show();
  }

// Set font

  public void changeFont(Font fnt) {
    font = fnt;
    Table wins = windows.keyNames();

    for (int i = 0; i < wins.size(); i++)
      ((MJEWindow) windows.get(wins.getString(i))).changeFont(fnt);
  }

// Open project

  public boolean open(String path) {
    try {
      project.clear();
      FileInputStream fs = new FileInputStream(path);
      BufferedInputStream bs = new BufferedInputStream(fs);
      DataInputStream ds = new DataInputStream(bs);

    // Read project

      main = ds.readLine();
      html = ds.readLine();
      cpath = ds.readLine();
      odir = ds.readLine();
      optimize = new Boolean(ds.readLine()).booleanValue();
      debug = new Boolean(ds.readLine()).booleanValue();
      String str = ds.readLine();
 
      while (str != null) {
	String[] item = new String[2];
	item[0] = str;
	item[1] = ds.readLine();

	project.add(item);
	str = ds.readLine();
      }

      fs.close();
    } catch (Exception err) {
      console.set("Cannot open file.");
      return false;
    }

    return true;
  }

// Save project

  public void save(String path) {
    try {
      console.set("Saving project...");

      FileOutputStream fs = new FileOutputStream(path);
      PrintStream ps = new PrintStream(fs);
      String ls = System.getProperty("line.separator");

      ps.println(main);
      ps.println(html);
      ps.println(cpath);
      ps.println(odir);
      ps.println(optimize);
      ps.println(debug);

      for (int i = 0; i < project.size(); i++) {
	String[] item = (String[]) project.get(i);
	ps.println(item[0]);
	ps.print(item[1]);
	ps.print(ls);
      }

      fs.close();
      console.set("Ready.");
    } catch (Exception err) {
      console.set("Cannot save project.");
    }
  }

// Compile file

  public void compile(String file, boolean thread) {
    try {
      MJECompiler compiler = new MJECompiler(this);
      compiler.set(file);
 
      if (thread)
	compiler.start();
      else
	compiler.run();
    }
    catch (Exception err) {
      console.add("Cannot compiling " + file + ".");
    }
  }

// Get window

  public MJEWindow getWindow(String file) {
    MJEWindow win;
    Hash wins = windows;
    String pth;

    if (new File(file).exists())
      pth = file;
    else if (new File(directory + file).exists())
      pth = directory + file;
    else
      return null;

    if (wins.containsKey(pth))
      win = (MJEWindow) wins.get(pth);
    else {
      win = new MJEWindow(file, font, this);
      win.open(pth);
      wins.put(pth, win);
    }

    return win;
  }

// Exit editor

  public void exit() {
    System.exit(0);
  }

// Handle system event

  public boolean handleEvent(Event evt) {
    if (evt.id == Event.WINDOW_DESTROY && evt.target == this)
      exit();
 
    return super.handleEvent(evt);
  }

// Handle component events

  public boolean action(Event evt, Object obj) {
    String label = (String) obj;
  
  // File menu

    if (label.equals(NEW_FILE)) {
      MJEWindow win = new MJEWindow("Untitled", font, this);
      win.editor = this;
      return true;
    }

    if (label.equals(OPEN_FILE)) {
      FileDialog dialog = new FileDialog(this, OPEN_FILE, FileDialog.LOAD);

      dialog.show();
      String file = dialog.getFile();

      if (file != null) {
	String path = dialog.getDirectory() + file;
	MJEWindow win = new MJEWindow(file, font, this);
        win.editor = this;
        win.open(path);
      }

      return true;
    }

    if (label.equals(NEW_PROJECT)) {
      FileDialog dialog = new FileDialog(this, NEW_PROJECT, FileDialog.LOAD);

      dialog.show();
      String file = dialog.getFile();

      if (file != null) {
	if (new File(file).exists()) {
	  console.add("Project already exists.");
	  return true;
	}

	if (file.endsWith(".project")) {
	  directory = dialog.getDirectory();
	  path = directory + file;

	  main = "";
	  html = "";
	  cpath = "";
  	  odir = "";
  	  optimize = false;
  	  debug = false;

	  setTitle(file + " - Mini Java Editor");
	  saveItem.enable();
	  saveAsItem.enable();
	  closeItem.enable();
	  editMenu.enable();
	  projectMenu.enable();
	  list.clear();
          project.clear();
	  console.set("Ready.");
	}
	else
	  console.set("Please use the file extension '.project'");
      }
      
      return true;
    }

    if (label.equals(OPEN_PROJECT)) {
      FileDialog dialog = new FileDialog(this, OPEN_PROJECT, FileDialog.LOAD);

      dialog.show();
      String file = dialog.getFile();

      if (file != null) {
	if (file.endsWith(".project")) {
	  directory = dialog.getDirectory();
	  path = directory + file;

	  if (open(path)) {
	    setTitle(file + " - Mini Java Editor");
	    saveItem.enable();
	    saveAsItem.enable();
	    closeItem.enable();
	    editMenu.enable();
	    projectMenu.enable();
	    list.refresh();
	    console.set("Ready.");
	  }
	}
      }

      return true;
    }

    if (label.equals(SAVE_PROJECT)) {
      if (path != null)
        save(path);
    }

    if (label.equals(SAVE_PROJECT_AS)) {
      FileDialog dialog = new FileDialog(this, SAVE_PROJECT_AS, 
	FileDialog.SAVE);
      
      dialog.setFile("Untitled.project");
      dialog.show();
      String file = dialog.getFile();

      if (file != null) {
	directory = dialog.getDirectory();
	path = directory + file;
	setTitle(file + " - Mini Java Editor");
	save(path);
      }

      return true;
    }

    if (label.equals(CLOSE_PROJECT)) {
      directory = null;
      list.clear();
      saveItem.disable();
      saveAsItem.disable();
      editMenu.disable();
      projectMenu.disable();
      project.clear();

      return true;
    }

    if (label.equals(PREFERENCES)) {
      if (prefDialog == null) {
      	prefDialog = new PreferencesDialog(this);
	prefDialog.pack();
      }

      prefDialog.show();
      return true;
    }

    if (label.equals(EXIT))
      exit();

  // Edit menu

    if (label.equals(ADD_FILE)) {
      FileDialog dialog = new FileDialog(this, ADD_FILE, FileDialog.LOAD);

      dialog.show();
      String file = dialog.getFile();

      if (file != null) {
	String dir = dialog.getDirectory();
	String str;
	String[] item = new String[2];

	if (dir.equals(directory)) 
	  item[0] = file;
	else
	  item[0] = dir + file;

	item[1] = "0";
	project.add(item);
	list.refresh();
      }

      return true;
    }

    if (label.equals(ADD_NEW_FILE)) {
      FileDialog dialog = new FileDialog(this, ADD_NEW_FILE, FileDialog.LOAD);

      dialog.show();
      String file = dialog.getFile();

      if (file != null) {
	if (new File(file).exists()) {
	  console.add("File already exists.");
	  return true;
	}

	String dir = dialog.getDirectory();
	String str;
	String[] item = new String[2];

	if (dir.equals(directory)) 
	  item[0] = file;
	else
	  item[0] = dir + file;

	item[1] = "0";
	project.add(item);
	list.refresh();

	try {
	  FileOutputStream fs = new FileOutputStream(item[0]);
	  fs.close();
	}
	catch (Exception err) {
          console.set("Cannot write file.");
        }
      }
    }

    if (label.equals(ADD_ALL_FILES)) {
      String str;
      String[] files = new File(directory).list();

      for (int i = 0; i < files.length; i++) {
	String[] item = new String[2];

	item[0] = files[i];
	item[1] = "0";
	project.add(item);
      }

      list.refresh();
      return true;
    }

   if (label.equals(ADD_ALL_JAVA_FILES)) {
      String str;
      String[] files = new File(directory).list();

      for (int i = 0; i < files.length; i++) {
	if (files[i].endsWith(".java")) {
	  String[] item = new String[2];

	  item[0] = files[i];
	  item[1] = "0";
	  project.add(item);
	}
      }

      list.refresh();
      return true;
    }

    if (label.equals(REMOVE_FILE)) {
      for (int i = 0; i < project.size(); i++) {
	String str = ((String[]) project.get(i))[0];

	if (str.equals(list.getSelectedItem())) {
	  project.removeAt(i);

	  if (windows.containsKey(str)) {
	    MJEWindow win = (MJEWindow) windows.get(str);
	    win.dispose();
	  }
	}
      }

      list.refresh();
      return true;
    }

    if (label.equals(REMOVE_ALL_FILES)) {
      for (int i = 0; i < project.size(); i++) {
        String str = ((String[]) project.get(i))[0];

	if (windows.containsKey(str)) {
	  MJEWindow win = (MJEWindow) windows.get(str);
	  win.dispose();
	}
      }

      project.clear();
      list.clear();
    }
     
    if (label.equals(MOVE_FILE_UP)) {
      int pos = list.getSelectedIndex();

      if (pos > 0) {
	String str = list.getItem(pos);
	list.delItem(pos);
	list.addItem(str, pos - 1);
	list.select(pos - 1);

	Object item = project.get(pos);
	project.removeAt(pos);
	project.addAt(item, pos - 1);
      }
    }

    if (label.equals(MOVE_FILE_DOWN)) {
      int pos = list.getSelectedIndex();

      if (pos > -1 || pos < (list.countItems() - 1)) {
	String str = list.getItem(pos);
	list.delItem(pos);
	list.addItem(str, pos + 1);
	list.select(pos + 1);

	Object item = project.get(pos);
	project.removeAt(pos);
	project.addAt(item, pos + 1);
      }
    }

    if (label.equals(SORT_FILES)) {
      for (int i = project.size() - 1; i > 0; i--) {
	for (int j = 0; j < i; j++) {
	  String[] file1 = (String[]) project.get(j);
	  String[] file2 = (String[]) project.get(j + 1);

	  if (file1[0].compareTo(file2[0]) > 0) {
	    project.removeAt(j);
	    project.addAt(file1, j + 1);
  	  }
        }
      }

      list.refresh();
    }

  // Project menu

    if (label.equals(COMPILE)) {
      String str = (String) list.getSelectedItem();

      if (str.endsWith(".java"))
	compile(str, true);
    }

    if (label.equals(BUILD)) {
      int build = 0;

      for (int i = 0; i < project.size(); i++) {
	String[] item = (String[]) project.get(i);

	if (item[0].endsWith(".java")) {
	  File file = new File(item[0]);
	  long time = file.lastModified();

	  if (time != new Long(item[1]).longValue()) {
	    compile(item[0], false);
	
	    if (compileError)
	      break;
	    else {
	      item[1] = new Long(time).toString();
	      build++;
	    }
	  }
	}
      }

      if (build > 0)
        save(path);

      return true;
    }

    if (label.equals(BUILD_ALL)) {
      boolean state = true;
      String str = (String) list.getSelectedItem();

      for (int i = 0; i < project.size(); i++) {
	String[] item = (String[]) project.get(i);

	if (item[0].endsWith(".java")) {
	  compile(item[0], false);

	  if (compileError) {
	    state = false;
	    break;
	  }
	  else
	    item[1] = new Long(new File(item[0]).lastModified()).toString();
	}
      }

      if (state)
        save(path);

      return true;
    }

    if (label.equals(RUN)) {
      String cname = main.trim();

      if (cname.length() > 0) {
        String fs = System.getProperty("file.separator");
	String ipath = System.getProperty("java.home") + fs + "bin" + fs +
	  "java ";

	try {
	  console.clear();
	  console.file = "";

          Process ps = Runtime.getRuntime().exec(ipath + cname);
          DataInputStream ds = new DataInputStream(ps.getInputStream());
          String str = ds.readLine();

          while (str != null) {
 	    console.add(str);
	    str = ds.readLine();
	  }
	}
	catch (Exception err) {
	  console.set("Problem running " + cname + ".");
	}
      }
      else
	console.set("No class to run.");
    }

    if (label.equals(RUN_APPLETVIEWER)) {
      String hname = html.trim();

      if (hname.length() > 0) {
        String fs = System.getProperty("file.separator");

	try {
	  Runtime.getRuntime().exec(System.getProperty("java.home") + fs + 
	    "bin" + fs + "appletviewer " + hname);
	}
	catch (Exception err) {
	  console.set("Problem running AppletViewer.");
	}
      }
      else
	console.set("Please specified the HTML file in Options menu");
    }

    if (label.equals(RUN_WEB_BROWSER)) {
      String hname = html.trim();

      if ((hname.length() > 0) && (browser.trim().length() > 0)) {
	try {
          String fs = System.getProperty("file.separator");
	  Runtime.getRuntime().exec(browser + " file://" + directory + fs
	    + hname);
	}
	catch (Exception err) {
	  console.set("Problem running web browser.");
	}
      }
      else {
	try {
	  Runtime.getRuntime().exec(browser);
	}
	catch (Exception err) {
  	  console.set("Problem running web browser.");
	}
      }
    }

    if (label.equals(GLOBAL_SEARCH)) {
      if (gsearchDialog == null) {
      	gsearchDialog = new GlobalSearchDialog(this);
	gsearchDialog.pack();
      }

      gsearchDialog.show();
      return true;
    }

    if (label.equals(OPTIONS)) {
      if (optionsDialog == null) {
      	optionsDialog = new OptionsDialog(this);
	optionsDialog.pack();
      }

      optionsDialog.show();
      return true;
    }
 
  // Help menu

    if (label.equals(ABOUT)) {
      if (aboutDialog == null) {
        aboutDialog = new InfoDialog(this, label, ABOUT_TEXT); 
	aboutDialog.pack();
     }

      aboutDialog.show();
      return true;
    }
    
    return false; 
  }
}

// 
// File list area
//

class MJEFiles extends List {
  MJE editor;

  public MJEFiles(MJE ed) {
    super();
    editor = ed;
  }

  public void clear() {
    delItems(0, countItems() - 1);
  }

  public void refresh() {
    clear();

    for (int i = 0; i < editor.project.size(); i++)
      addItem(((String[]) editor.project.get(i))[0]);
  }

  public boolean action(Event evt, Object obj) {
    MJEWindow win = editor.getWindow((String) obj);

    if (win != null)
      win.show();
    else
      editor.console.set("Cannot find " + obj + ".");

    return true;
  }
}

//
// Console
//

class MJEConsole extends List implements MJELabels {
  MJE editor;
  String file = "";

  public MJEConsole(MJE ed) {
    super();
    editor = ed;
    set(ABOUT_TEXT);
  }

  public void clear() {
    set("Ready.");
  }

  public void set(String str) {
    delItems(0, countItems() - 1);
    addItem(str);
  }

  public void add(String str) {
    addItem(str);
  }

  public boolean action(Event evt, Object obj) {
    if (file.length() > 0) {
      String str = (String) obj;

      if (str.charAt(0) == '*') {
        str = getItem(getSelectedIndex() + 1);
      }

      if (str.charAt(0) == ' ') { 
        for (int i = 4; i < 8; i++)
          if (str.charAt(i) == ' ') {
	    int line = Integer.parseInt(str.substring(3, i));
	    int col = 0;

	    for (int j = i + 2; j < i + 5; j++)
	      if (str.charAt(j) == ')') 
	        col = Integer.parseInt(str.substring(i + 2, j));

	    MJEWindow win = editor.getWindow(file);

	    if (win != null)
	      win.gotoLine(line, col);

	    break;
          }
      }
    }

    return true;
  }
}

