/*
Storybook: Open Source software for novelists and authors.
Copyright (C) 2008 - 2012 Martin Mustun

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package storybook.model.book;

import i18n.I18N;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import org.hibernate.Session;
import storybook.App;
import storybook.Pref;
import storybook.model.H2File;
import storybook.model.Model;
import storybook.model.hbn.dao.*;
import storybook.model.hbn.entity.AbstractEntity;
import storybook.model.hbn.entity.Attribute;
import storybook.model.hbn.entity.Category;
import storybook.model.hbn.entity.Chapter;
import storybook.model.hbn.entity.Endnote;
import storybook.model.hbn.entity.Episode;
import storybook.model.hbn.entity.Event;
import storybook.model.hbn.entity.Gender;
import storybook.model.hbn.entity.Idea;
import storybook.model.hbn.entity.Item;
import storybook.model.hbn.entity.Itemlink;
import storybook.model.hbn.entity.Location;
import storybook.model.hbn.entity.Memo;
import storybook.model.hbn.entity.Part;
import storybook.model.hbn.entity.Person;
import storybook.model.hbn.entity.Plot;
import storybook.model.hbn.entity.Relationship;
import storybook.model.hbn.entity.Scene;
import storybook.model.hbn.entity.Strand;
import storybook.model.hbn.entity.Tag;
import storybook.model.hbn.entity.Taglink;
import storybook.tools.Markdown;
import storybook.tools.StringUtil;
import storybook.tools.file.EnvUtil;
import storybook.tools.file.FileFilter;
import storybook.ui.MainFrame;

/**
 * some utilities for managing Book project
 *
 * @author martin
 *
 */
public class BookUtil {

	private static final String TT = "BookUtil";

	private BookUtil() {
		// nothing
	}

	/**
	 * compute the Objective chars for Part or Chapter
	 *
	 * @param nature
	 * @param nb
	 * @return
	 */
	public static int computeObjectiveChars(int nature, int nb) {
		if (nature == 0 || nb == 0) {
			return 0;
		}
		String s = I18N.getMsg("book.nature." + nature);
		if (s.startsWith("!")) {
			return 0;
		}
		if (!s.contains(":")) {
			return 0;
		}
		s = s.substring(s.indexOf(":") + 1);
		if (!StringUtil.isNumeric(s)) {
			return 0;
		}
		return Integer.parseInt(s) / nb;
	}

	/**
	 * compute the Book size
	 *
	 * @param mainFrame
	 * @return
	 */
	public static Map<Object, Integer> getBookSize(MainFrame mainFrame) {
		Map<Object, Integer> sizes = new HashMap<>();
		// Get scenes
		sizes.clear();
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		PartDAO partdao = new PartDAO(session);
		List<Part> parts = partdao.findAllRoots();
		session.close();
		for (Part part : parts) {
			appendPartSizes(mainFrame, part, sizes);
		}
		return sizes;
	}

	private static int appendPartSizes(MainFrame mainFrame, Part part, Map<Object, Integer> sizes) {
		int ret = 0;
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		PartDAO partdao = new PartDAO(session);
		List<Part> subparts = partdao.getParts(part);
		List<Chapter> chapters = partdao.findChapters(part);
		session.close();
		for (Part subpart : subparts) {
			ret += appendPartSizes(mainFrame, subpart, sizes);
		}
		for (Chapter chapter : chapters) {
			int chapterSize = appendChapterSizes(mainFrame, chapter, sizes);
			ret += chapterSize;
		}
		sizes.put(part, ret);
		return ret;
	}

	private static int appendChapterSizes(MainFrame mainFrame, Chapter chapter, Map<Object, Integer> sizes) {
		int ret = 0;
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		SceneDAO dao = new SceneDAO(session);
		List<Scene> scenes = dao.findByChapter(chapter);
		session.close();
		for (Scene scene : scenes) {
			int sceneSize = scene.getWords();
			sizes.put(scene, sceneSize);
			ret += sceneSize;
		}
		sizes.put(chapter, ret);
		return ret;
	}

	/**
	 * open the Book project
	 *
	 * @return a H2File if OK, else null
	 */
	public static H2File openDialog() {
		final JFileChooser fc = new JFileChooser();
		fc.setCurrentDirectory(new File(App.preferences.getString(Pref.KEY.LASTOPEN_DIR, EnvUtil.getHomeDir().getAbsolutePath())));
		FileFilter filter = new FileFilter("db");
		fc.addChoosableFileFilter(filter);
		fc.setFileFilter(filter);
		int ret = fc.showOpenDialog(null);
		if (ret == JFileChooser.APPROVE_OPTION) {
			File file = fc.getSelectedFile();
			if (!file.exists()) {
				JOptionPane.showMessageDialog(null,
						I18N.getMsg("project.not.exist.text", file),
						I18N.getMsg("project.not.exist.title"),
						JOptionPane.ERROR_MESSAGE);
				return null;
			}
			H2File dbFile = new H2File(file);
			return dbFile;
		}
		return null;
	}

	/**
	 * get a new Entityforthe given MainFrame
	 *
	 * @param mainFrame
	 * @param objtype : type of entity
	 * @return
	 */
	public static AbstractEntity getNewEntity(MainFrame mainFrame, Book.TYPE objtype) {
		//LOG.trace(TT + ".getNewEntity(mainFrame, type=" + objtype.name() + ")");
		AbstractEntity entity;
		switch (objtype) {
			case ATTRIBUTE:
			case ATTRIBUTES:
				entity = new Attribute();
				break;
			case CATEGORY:
			case CATEGORIES:
				entity = new Category();
				break;
			case CHAPTER:
			case CHAPTERS:
				entity = new Chapter();
				break;
			case ENDNOTE:
			case ENDNOTES:
				entity = new Endnote();
				break;
			case EPISODE:
			case EPISODES:
				entity = new Episode();
				break;
			case EVENT:
			case EVENTS:
				entity = new Event();
				break;
			case GENDER:
			case GENDERS:
				entity = new Gender();
				break;
			case IDEA:
			case IDEAS:
				entity = new Idea();
				break;
			case ITEM:
			case ITEMS:
				entity = new Item();
				break;
			case ITEMLINK:
			case ITEMLINKS:
				entity = new Itemlink();
				break;
			case LOCATION:
			case LOCATIONS:
				entity = new Location();
				break;
			case MEMO:
			case MEMOS:
				entity = new Memo();
				break;
			case PART:
			case PARTS:
				entity = new Part();
				break;
			case PERSON:
			case PERSONS:
				entity = new Person();
				break;
			case PLOT:
			case PLOTS:
				entity = new Plot();
				break;
			case RELATION:
			case RELATIONS:
				entity = new Relationship();
				break;
			case SCENE:
			case SCENES:
				entity = new Scene(mainFrame);
				break;
			case STRAND:
			case STRANDS:
				entity = new Strand();
				break;
			case TAG:
			case TAGS:
				entity = new Tag();
				break;
			case TAGLINK:
			case TAGLINKS:
				entity = new Taglink();
				break;
			default:
				return null;
		}
		return entity;
	}

	/**
	 * get number of characters of a list of Chapters
	 *
	 * @param mainFrame
	 * @param chapters
	 *
	 * @return
	 */
	public static int getNbChars(MainFrame mainFrame, List<Chapter> chapters) {
		int nb = 0;
		for (Chapter chapter : chapters) {
			nb += BookUtil.getNbChars(mainFrame, chapter);
		}
		return nb;
	}

	/**
	 * get number of characters of a given Chapter
	 *
	 * @param mainFrame
	 * @param chapter
	 *
	 * @return
	 */
	public static Integer getNbChars(MainFrame mainFrame, Chapter chapter) {
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		SceneDAO dao = new SceneDAO(session);
		List<Scene> scenes = dao.findAll();
		session.close();
		int nb = 0;
		for (Scene scene : scenes) {
			boolean b = false;
			if (chapter == null) {
				b = true;
			} else if (scene.getChapter() != null && scene.getChapter().equals(chapter)) {
				b = true;
			}
			if (b) {
				nb += scene.getChars();
			}
		}
		return nb;
	}

	/**
	 * get number of characters of all scenes
	 *
	 * @param mainFrame
	 *
	 * @return
	 */
	public static Integer getNbChars(MainFrame mainFrame) {
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		SceneDAO dao = new SceneDAO(session);
		List<Scene> scenes = dao.findAll();
		session.close();
		int nb = 0;
		for (Scene scene : scenes) {
			nb += scene.getChars();
		}
		return nb;
	}

	/**
	 * get number of words in a list of Chapters
	 *
	 * @param mainFrame
	 * @param chapters : the list of chapters
	 *
	 * @return
	 */
	public static int getNbWords(MainFrame mainFrame, List<Chapter> chapters) {
		int nb = 0;
		for (Chapter chapter : chapters) {
			nb += BookUtil.getNbWords(mainFrame, chapter);
		}
		return nb;
	}

	/**
	 * get number of words in a given Chapter
	 *
	 * @param mainFrame
	 * @param chapter
	 *
	 * @return
	 */
	public static Integer getNbWords(MainFrame mainFrame, Chapter chapter) {
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		PartDAO partdao = new PartDAO(session);
		partdao.findAllRoots();
		SceneDAO dao = new SceneDAO(session);
		List<Scene> scenes = dao.findAll();
		session.close();
		int nb = 0;
		for (Scene s : scenes) {
			if ((chapter == null && s.getChapter() == null)
					|| (s.getChapter() != null && s.getChapter().equals(chapter))) {
				nb += s.getWords();
			}
		}
		return nb;
	}

	/**
	 * get number of words in all Scenes
	 *
	 * @param mainFrame
	 *
	 * @return
	 */
	public static Integer getNbWords(MainFrame mainFrame) {
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		SceneDAO dao = new SceneDAO(session);
		List<Scene> scenes = dao.findAll();
		session.close();
		int nb = 0;
		for (Scene scene : scenes) {
			nb += scene.getWords();
		}
		return nb;
	}

	/**
	 * convert format for summary, description, notes (HTML<>Markdown, Marksown<>HTML
	 *
	 * @param mainFrame
	 * @param b : true for HTML<>Marxkdown
	 */
	public static void convertTo(MainFrame mainFrame, boolean b) {
		// when b is true convert to Markdown else convert to HTML
		Model model = mainFrame.getBookModel();
		Session session;
		session = model.beginTransaction();
		convertEntity(b, session, new AttributeDAO(session));
		convertEntity(b, session, new CategoryDAO(session));
		convertEntity(b, session, new ChapterDAO(session));
		convertEntity(b, session, new EndnoteDAO(session));
		convertEntity(b, session, new GenderDAO(session));
		convertEntity(b, session, new IdeaDAO(session));
		convertEntity(b, session, new ItemDAO(session));
		convertEntity(b, session, new ItemlinkDAO(session));
		convertEntity(b, session, new LocationDAO(session));
		convertEntity(b, session, new MemoDAO(session));
		convertEntity(b, session, new PartDAO(session));
		convertEntity(b, session, new PersonDAO(session));
		convertEntity(b, session, new PlotDAO(session));
		convertEntity(b, session, new RelationDAO(session));
		convertEntity(b, session, new SceneDAO(session));
		convertEntity(b, session, new StrandDAO(session));
		convertEntity(b, session, new TagDAO(session));
		convertEntity(b, session, new TaglinkDAO(session));
		convertEntity(b, session, new EventDAO(session));
		session.close();
	}

	/**
	 * convert format for summary, description, notes (HTML<>Markdown, Marksown<>HTML)
	 *
	 * @param b : true for HTML<>Marxkdown
	 * @param session
	 * @param dao
	 */
	public static void convertEntity(boolean b, Session session, _GenericDAO dao) {
		dao.setSession(session);
		List entities = dao.findAll();
		for (Object obj : entities) {
			boolean c = false;
			AbstractEntity entity = (AbstractEntity) obj;
			if (b && entity.getNotes().contains("</p>")) {
				entity.setNotes(Markdown.toMarkdown(entity.getNotes()));
				c = true;
			}
			if (!b && !entity.getNotes().contains("</p>")) {
				entity.setNotes(Markdown.toHtml(entity.getNotes()));
				c = true;
			}
			if (b && entity.getDescription().contains("</p>")) {
				entity.setDescription(Markdown.toMarkdown(entity.getDescription()));
				c = true;
			}
			if (!b && !entity.getDescription().contains("</p>")) {
				entity.setDescription(Markdown.toHtml(entity.getDescription()));
				c = true;
			}
			if (entity instanceof Scene) {
				if (b && ((Scene) entity).getSummary().contains("</p>")) {
					((Scene) entity).setSummary(Markdown.toMarkdown(((Scene) entity).getSummary()));
					c = true;
				}
				if (!b && !((Scene) entity).getSummary().contains("</p>")) {
					((Scene) entity).setSummary(Markdown.toHtml(((Scene) entity).getSummary()));
					c = true;
				}
			}
			if (c) {
				session.update(entity);
			}
		}
	}

}
