/* (non-Javadoc)
   * @see org.nightlabs.jfire.geography.GeographyTemplateDataManagerRemote#storeGeographyTemplateDistrictData(org.nightlabs.jfire.geography.District)
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  @RolesAllowed("_Guest_")
  public void storeGeographyTemplateDistrictData(final District storedDistrict)
      throws IOException, SecurityException {
    assertWritingAllowed();

    final PersistenceManager pm = createPersistenceManager();
    try {
      pm.getFetchPlan().setMaxFetchDepth(1);
      pm.getFetchPlan().setGroup(FetchPlan.ALL);

      // initialize:
      Geography.sharedInstance();

      String rootOrganisationID;

      try {
        final InitialContext initialContext = new InitialContext();
        try {
          rootOrganisationID = Organisation.getRootOrganisationID(initialContext);
        } // try
        finally {
          initialContext.close();
        } // finally
      } // try
      catch (final NamingException x) {
        throw new RuntimeException(
            x); // it's definitely an unexpected exception if we can't access the local JNDI.
      } // catch

      final CountryID countryID = CountryID.create(storedDistrict.getCountryID());

      final ByteArrayOutputStream out = new ByteArrayOutputStream();
      final Writer w = new OutputStreamWriter(new DeflaterOutputStream(out), IOUtil.CHARSET_UTF_8);
      try {
        w.write(DISTRICT_CSV_HEADER);
        // TODO: write all old data!
        if (storedDistrict != null) {
          final String csvLines = GeographyImplResourceCSV.district2csvLines(storedDistrict);

          if (logger.isDebugEnabled()) logger.debug(csvLines);

          //					csvLines.trim();
          w.write(csvLines);
        } // if
      } // try
      finally {
        w.close();
      } // finally

      CSV.setCSVData(
          pm, rootOrganisationID, CSV.CSV_TYPE_DISTRICT, countryID.countryID, out.toByteArray());

      clearCache();
    } // try
    finally {
      pm.close();
    } // finally
  }
  /* (non-Javadoc)
   * @see org.nightlabs.jfire.geography.GeographyTemplateDataManagerRemote#initialise()
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  @RolesAllowed("_System_")
  public void initialise() throws Exception {
    final Geography geography = Geography.sharedInstance();
    final String organisationID = getOrganisationID();

    final PersistenceManager pm = createPersistenceManager();
    try {
      // As the ModuleMetaData is not managed by GeographyManagerBean, we can do it here (this stuff
      // is expensive and we should therefore avoid to
      // run it on every boot).
      ModuleMetaData moduleMetaData =
          ModuleMetaData.getModuleMetaData(pm, JFireGeographyEAR.MODULE_NAME);
      if (moduleMetaData != null) return;

      // version is {major}.{minor}.{release}-{patchlevel}-{suffix}
      moduleMetaData =
          ModuleMetaData.createModuleMetaDataFromManifest(
              JFireGeographyEAR.MODULE_NAME, JFireGeographyEAR.class);
      pm.makePersistent(moduleMetaData);

      iterateCountries:
      for (final Country country : geography.getCountries()) {
        // CityIDs are local within the namespace of countryID and organisationID

        final IDNamespace namespace =
            IDNamespace.getIDNamespace(
                pm, organisationID, City.class.getName() + "#" + country.getCountryID());
        long nextID = namespace.getNextID();
        if (nextID != 0) continue iterateCountries; // already initialised - skip this country

        nextID = -1;

        for (final Region region : geography.getRegions(CountryID.create(country), true)) {
          iterateCities:
          for (final City city : geography.getCities(RegionID.create(region), true)) {
            if (!organisationID.equals(city.getOrganisationID())) continue iterateCities;

            long cityID;
            try {
              cityID = Long.parseLong(city.getCityID());
            } catch (final NumberFormatException x) {
              continue iterateCities;
            }

            nextID = Math.max(nextID, cityID);
          } // iterateCities: for (City city : geography.getCities(RegionID.create(region), true)) {
        } // for (Region region : geography.getRegions(CountryID.create(country), true)) {

        namespace.setNextID(++nextID);
      } // iterateCountries: for (Country country : geography.getCountries()) {

      // TODO initialise the namespaces for Regions, Locations etc.
      // note that a countryID is defined by the ISO standard and thus does NOT require ID
      // generation!
    } finally {
      pm.close();
    }
  }
  /* (non-Javadoc)
   * @see org.nightlabs.jfire.geography.GeographyTemplateDataManagerRemote#storeGeographyTemplateLocationData(org.nightlabs.jfire.geography.Location)
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  @RolesAllowed("_Guest_")
  public void storeGeographyTemplateLocationData(Location storedLocation)
      throws IOException, SecurityException {
    assertWritingAllowed();

    final PersistenceManager pm = createPersistenceManager();
    try {
      NLJDOHelper.enableTransactionSerializeReadObjects(pm);
      try {
        pm.getFetchPlan().setMaxFetchDepth(1);
        pm.getFetchPlan().setGroup(FetchPlan.ALL);

        final Geography geography = Geography.sharedInstance();
        geography
            .clearCache(); // ensure that the data we're going to manipulate in this transaction are
                           // really up-to-date.

        String rootOrganisationID;

        try {
          final InitialContext initialContext = new InitialContext();
          try {
            rootOrganisationID = Organisation.getRootOrganisationID(initialContext);
          } finally {
            initialContext.close();
          }
        } catch (final NamingException x) {
          throw new RuntimeException(
              x); // it's definitely an unexpected exception if we can't access the local JNDI.
        }

        LocationID locationID =
            LocationID.create(
                storedLocation.getCountryID(), rootOrganisationID, storedLocation.getLocationID());
        final CountryID countryID = CountryID.create(storedLocation.getCountryID());

        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final Writer w =
            new OutputStreamWriter(new DeflaterOutputStream(out), IOUtil.CHARSET_UTF_8);
        try {
          w.write(LOCATION_CSV_HEADER);
          for (final Region r : geography.getRegions(countryID, true)) {
            for (final City c : geography.getCities(RegionID.create(r), true)) {
              for (Location existLocation : geography.getLocations(CityID.create(c), true)) {
                if (LocationID.create(
                        existLocation.getCountryID(),
                        rootOrganisationID,
                        existLocation.getLocationID())
                    .equals(locationID)) {
                  existLocation = storedLocation;
                  storedLocation = null;
                  locationID = null;
                } // if

                final String csvLines = GeographyImplResourceCSV.location2csvLines(existLocation);

                if (logger.isDebugEnabled()) logger.debug(csvLines);

                w.write(csvLines);
              } // for
            } // for
          } // for

          if (storedLocation != null) {
            final String csvLines = GeographyImplResourceCSV.location2csvLines(storedLocation);

            if (logger.isDebugEnabled()) logger.debug(csvLines);

            csvLines.trim();
            w.write(csvLines);
          } // if
        } finally {
          w.close();
        }

        CSV.setCSVData(
            pm, rootOrganisationID, CSV.CSV_TYPE_LOCATION, countryID.countryID, out.toByteArray());

        clearCache();

      } finally {
        NLJDOHelper.disableTransactionSerializeReadObjects(pm);
      }
    } finally {
      pm.close();
    }
  }
  /* (non-Javadoc)
   * @see org.nightlabs.jfire.geography.GeographyTemplateDataManagerRemote#storeGeographyTemplateCityData(org.nightlabs.jfire.geography.City)
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  @RolesAllowed("_Guest_")
  public void storeGeographyTemplateCityData(City storedCity)
      throws IOException, SecurityException {
    assertWritingAllowed();

    final PersistenceManager pm = createPersistenceManager();
    try {
      pm.getFetchPlan().setMaxFetchDepth(1);
      pm.getFetchPlan().setGroup(FetchPlan.ALL);

      final Geography geography = Geography.sharedInstance();

      String rootOrganisationID;

      try {
        final InitialContext initialContext = new InitialContext();
        try {
          rootOrganisationID = Organisation.getRootOrganisationID(initialContext);
        } // try
        finally {
          initialContext.close();
        } // finally
      } // try
      catch (final NamingException x) {
        throw new RuntimeException(
            x); // it's definitely an unexpected exception if we can't access the local JNDI.
      } // catch

      CityID cityID = CityID.create(storedCity);
      final CountryID countryID = CountryID.create(storedCity);

      final ByteArrayOutputStream out = new ByteArrayOutputStream();
      final Writer w = new OutputStreamWriter(new DeflaterOutputStream(out), IOUtil.CHARSET_UTF_8);
      try {
        w.write(CITY_CSV_HEADER);
        for (final Region existRegion : geography.getRegions(countryID, true)) {
          for (City existCity : geography.getCities(RegionID.create(existRegion), true)) {
            if (CityID.create(existCity).equals(cityID)) { // if come in this case it is update
              existCity = storedCity;
              storedCity = null;
              cityID = null;
            } // if

            final String csvLines = GeographyImplResourceCSV.city2csvLines(existCity);

            if (logger.isDebugEnabled()) logger.debug(csvLines);

            w.write(csvLines);
          } // for
        } // for

        if (storedCity != null) { // add new city
          final String csvLines = GeographyImplResourceCSV.city2csvLines(storedCity);

          if (logger.isDebugEnabled()) logger.debug(csvLines);

          csvLines.trim();
          w.write(csvLines);
        } // if
      } // try
      finally {
        w.close();
      } // finally

      CSV.setCSVData(
          pm, rootOrganisationID, CSV.CSV_TYPE_CITY, countryID.countryID, out.toByteArray());

      clearCache();
    } // try
    finally {
      pm.close();
    } // finally
  }