/*
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.hbn.entity;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Session;
import org.w3c.dom.Node;
import storybook.model.DB.DATA;
import storybook.model.Model;
import storybook.model.book.Book;
import storybook.model.hbn.dao.LocationDAO;
import storybook.tools.LOG;
import storybook.tools.xml.XmlKey.XK;
import storybook.tools.xml.XmlUtil;
import storybook.ui.MainFrame;
import storybook.ui.dialog.ExceptionDlg;

/**
 * Location generated by hbm2java
 *
 * @hibernate.class table="LOCATION"
 */
public class Location extends AbstractEntity {

	public static List<String> findCities(MainFrame mainFrame) {
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		LocationDAO dao = new LocationDAO(session);
		List<String> cities = dao.findCities();
		model.commit();
		return (cities);
	}

	public static List<String> findCountries(MainFrame mainFrame) {
		Model model = mainFrame.getBookModel();
		Session session = model.beginTransaction();
		LocationDAO dao = new LocationDAO(session);
		List<String> countries = dao.findCountries();
		model.commit();
		return (countries);
	}

	private String address = "";
	private String city = "";
	private String country = "";
	private Integer altitude = 0;
	private String gps = "";
	private Location site = null;

	public Location() {
		super(Book.TYPE.LOCATION, "111");
	}

	public Location(ResultSet rs) {
		super(Book.TYPE.LOCATION, "111", rs);
		try {
			address = rs.getString("address");
			city = rs.getString("city");
			country = rs.getString("country");
			altitude = rs.getInt("altitude");
			gps = rs.getString("gps");
			site = rs.getObject("site", Location.class);
		} catch (SQLException ex) {
			//empty
		}
	}

	public Location(String name, String address, String city, String country,
	   Integer altitude, String description, String notes, Location site) {
		this();
		setName(name);
		this.address = address;
		this.city = city;
		this.country = country;
		this.altitude = altitude;
		setDescription(description);
		setNotes(notes);
		this.site = site;
		setAssistant("");
	}

	public String getCity() {
		return this.city;
	}

	public void setCity(String city) {
		this.city = (city == null ? "" : city);
	}

	public boolean hasCity() {
		return !((this.city == null) || (this.city.isEmpty()));
	}

	public String getCountry() {
		return this.country;
	}

	public String getCountryCity() {
		if (hasSite()) {
			return getSite().getFullName();
		} else {
			if (hasCountry()) {
				return this.country + ", " + this.city;
			}
			return this.city;
		}
	}

	public void setCountry(String country) {
		this.country = (country == null ? "" : country);
	}

	public boolean hasCountry() {
		return !((this.country == null) || (this.country.isEmpty()));
	}

	public String getAddress() {
		return this.address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Integer getAltitude() {
		return this.altitude;
	}

	public void setAltitude(Integer altitude) {
		this.altitude = altitude;
	}

	public Location getSite() {
		return this.site;
	}

	public boolean hasSite() {
		return site != null;
	}

	public void setSite(Location site) {
		if (site == null) {
			this.site = null;
			return;
		}
		// verify short loop - this is his new father
		if (this.equals(site)) {
			return;
		}

		// verify long loop - this is in the tree in his new father.
		Location father = site.getSite();
		while (father != null) {
			if (this.equals(father)) {
				return;
			}
			father = father.getSite();
		}

		// it's OK.
		this.site = site;

		try {
			// Compatibility with earlier oStorybook versions (9.4.17 or earlier)
			if (site.hasCity()) {
				// the location belongs to a site in a city
				// thus it will have the same "old hierarchy".
				setCity(site.getCity());
				setCountry(site.getCountry());
			} else if (site.hasCountry()) {
				// the location belongs to a site that is a city
				// thus it will be inside it for "old hierarchy".
				setCity(site.getName());
				setCountry(site.getCountry());
			} else {
				// the location belongs to a site that is a country
				// thus it will be a city inside it for "old hierarchy".
				setCity(null);
				//setCountry(site.getName());
				setCountry(null);
			}
		} catch (Exception e) {
			ExceptionDlg.show(this.getClass().getSimpleName()
			   + ".setSite(site=" + LOG.trace(site)
			   + ") " + e.getLocalizedMessage(), e);
		}
	}

	public void setSite() {
		this.site = null;
	}

	public String getGps() {
		return (gps == null ? "" : gps);
	}

	public void setGps(String gps) {
		this.gps = gps;
	}

	public String getFullName() {
		return getFullName(false);
	}

	public String getFullName(boolean reverse) {
		StringBuilder buf = new StringBuilder();
		if (reverse) {
			if (hasCountry()) {
				buf.append(country).append(": ");
			}
			if (hasCity()) {
				buf.append(city).append(": ");
			}
			if (hasSite()) {
				buf.append(getFullParentName()).append(": ");
			}
			buf.append(getName());
			return buf.toString();
		}
		buf.append(getName());
		if (hasSite()) {
			buf.append(", ").append(getFullParentName());
		}
		if (hasCity()) {
			buf.append(", ").append(city);
		}
		if (hasCountry()) {
			buf.append(", ").append(country);
		}
		return buf.toString();
	}

	public String getFullParentName() {
		if (!hasSite()) {
			return "";
		}
		String parent = hasSite() ? getSite().getName() : null;
		boolean isCity = hasCity() ? city.equals(parent) : false;
		boolean isCountry = hasCountry() ? country.equals(parent) : false;
		if (!isCity && !isCountry) {
			String lname = site.hasSite() ? getSite().getFullParentName() + "," : "";
			return lname + site.getName();
		}
		return "";
	}

	@Override
	public String toCsv(String quoteStart, String quoteEnd, String separator) {
		StringBuilder b = new StringBuilder();
		b.append(quoteStart).append(getClean(this)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(getName())).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(address)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(city)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(country)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(altitude)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(gps)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(site)).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(getDescription())).append(quoteEnd).append(separator);
		b.append(quoteStart).append(getClean(getNotes())).append(quoteEnd).append("\n");
		return (b.toString());
	}

	@Override
	public String toHtml() {
		return (toCsv("<td>", "</td>", "\n"));
	}

	@Override
	public String toText() {
		return (toCsv("", "", "\t"));
	}

	@Override
	public String toDetail(Integer detailed) {
		StringBuilder b = new StringBuilder();
		b.append(toDetailHeader(detailed));
		b.append(getInfo(detailed, DATA.LOCATION_ADDRESS, getAddress()));
		b.append(getInfo(detailed, DATA.LOCATION_CITY, getCity()));
		b.append(getInfo(detailed, DATA.LOCATION_COUNTRY, getCountry()));
		b.append(getInfo(detailed, DATA.LOCATION_ALTITUDE, getAltitude()));
		b.append(getInfo(detailed, DATA.LOCATION_GPS, getGps()));
		if (getSite() != null) {
			b.append(getInfo(2, DATA.LOCATION_SITE, getSite()));
		}
		b.append(toDetailFooter(detailed));
		return (b.toString());
	}

	@Override
	public String toXml() {
		StringBuilder b = new StringBuilder(toXmlBeg());
		b.append(XmlUtil.setAttribute(2, XK.ADDRESS, getAddress()));
		b.append(XmlUtil.setAttribute(0, XK.CITY, getCity()));
		b.append(XmlUtil.setAttribute(0, XK.COUNTRY, getCountry()));
		b.append(XmlUtil.setAttribute(2, XK.ALTITUDE, getClean(getAltitude())));
		b.append(XmlUtil.setAttribute(0, XK.GPS, getClean(getGps())));
		if (getSite() != null) {
			b.append(XmlUtil.setAttribute(0, XK.SUP, getClean(getSite().getId())));
		}
		b.append(">\n");
		b.append(toXmlEnd());
		return b.toString();
	}

	public static Location fromXml(Node node) {
		Location p = new Location();
		fromXmlBeg(node, p);
		p.setAddress(XmlUtil.getString(node, XK.ADDRESS));
		p.setCity(XmlUtil.getString(node, XK.CITY));
		p.setCountry(XmlUtil.getString(node, XK.COUNTRY));
		p.setAltitude(XmlUtil.getInteger(node, XK.ALTITUDE));
		p.setGps(XmlUtil.getString(node, XK.GPS));
		fromXmlEnd(node, p);
		return (p);
	}

	@Override
	@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
	public boolean equals(Object obj) {
		if (!super.equals(obj)) {
			return false;
		}
		Location test = (Location) obj;
		boolean ret = true;
		ret = ret && equalsStringNullValue(getName(), test.getName());
		ret = ret && equalsStringNullValue(getAddress(), test.getAddress());
		ret = ret && equalsStringNullValue(getCity(), test.getCity());
		ret = ret && equalsStringNullValue(getCountry(), test.getCountry());
		if (getSite() != null && test.getSite() != null) {
			ret = ret && (getSite().equals(test.getSite()));
		} else if (getSite() == null && test.getSite() == null) {
			ret = ret && true;
		}
		ret = ret && equalsIntegerNullValue(getAltitude(), test.getAltitude());
		ret = ret && equalsStringNullValue(getGps(), test.getGps());
		ret = ret && equalsStringNullValue(getDescription(), test.getDescription());
		ret = ret && equalsStringNullValue(getNotes(), test.getNotes());
		return ret;
	}

	@Override
	public int hashCode() {
		int hash = super.hashCode();
		hash = hash * 31 + (address != null ? address.hashCode() : 0);
		hash = hash * 31 + (city != null ? city.hashCode() : 0);
		hash = hash * 31 + (country != null ? country.hashCode() : 0);
		hash = hash * 31 + (altitude != null ? altitude.hashCode() : 0);
		hash = hash * 31 + (gps != null ? gps.hashCode() : 0);
		hash = hash * 31 + (site != null ? site.hashCode() : 0);
		return hash;
	}

	@Override
	public int compareTo(AbstractEntity en) {
		Location o = (Location) en;
		if (hasSite() && (!o.hasSite())) {
			return 1;
		}
		if (!hasSite() && (o.hasSite())) {
			return -1;
		}
		if (hasSite() && (o.hasSite()) && (!(getSite().equals(o.getSite())))) {
			return getSite().compareTo(o.getSite());
		}
		if (country == null && o.getCountry() == null) {
			return getName().compareTo(o.getName());
		}
		if (country != null && o.getCountry() == null) {
			return -1;
		}
		if (o.getCountry() != null && country == null) {
			return -1;
		}
		int cmp = country.compareTo(o.getCountry());
		if (cmp == 0) {
			if (city == null && o.getCity() == null) {
				return 0;
			}
			if (city != null && o.getCity() == null) {
				return -1;
			}
			if (o.getCity() != null && city == null) {
				return -1;
			}
			int cmp2 = city.compareTo(o.getCity());
			if (cmp2 == 0) {
				return getName().compareTo(o.getName());
			}
			return cmp2;
		}
		return cmp;
	}

	public static Location find(List<Location> list, String str) {
		for (Location elem : list) {
			if (elem.getName().equals(str)) {
				return (elem);
			}
		}
		return (null);
	}

	public static Location find(List<Location> list, Long id) {
		for (Location elem : list) {
			if (elem.id.equals(id)) {
				return (elem);
			}
		}
		return (null);
	}

	public static List<String> getDefColumns() {
		List<String> list = AbstractEntity.getDefColumns(Book.TYPE.LOCATION);
		list.add("address, 256");
		list.add("city, 256");
		list.add("country, 256");
		list.add("gps, 256");
		list.add("address, 256");
		return (list);
	}

	public static List<String> getTable() {
		List<String> ls = new ArrayList<>();
		String tableName = "location";
		AbstractEntity.getTable(tableName, ls);
		ls.add(tableName + ",address,String,256");
		ls.add(tableName + ",city,String,256");
		ls.add(tableName + ",country,String,256");
		ls.add(tableName + ",altitude,Integer,0");
		ls.add(tableName + ",gps,String,256");
		ls.add(tableName + ",location_id,Integer,0");
		return (ls);
	}

}
