package liquibase.ext.spatial.sqlgenerator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeSet;

import liquibase.database.Database;
import liquibase.database.core.DerbyDatabase;
import liquibase.database.core.H2Database;
import liquibase.datatype.LiquibaseDataType;
import liquibase.exception.ValidationErrors;
import liquibase.ext.spatial.datatype.GeometryType;
import liquibase.sql.Sql;
import liquibase.sqlgenerator.SqlGenerator;
import liquibase.sqlgenerator.SqlGeneratorChain;
import liquibase.sqlgenerator.core.AbstractSqlGenerator;
import liquibase.sqlgenerator.core.CreateTableGenerator;
import liquibase.statement.core.AddColumnStatement;
import liquibase.statement.core.CreateTableStatement;

/**
 * <code>CreateSpatialTableGeneratorGeoDB</code> augments the built-in {@link CreateTableGenerator}
 * by invoking the <code>AddGeometryColumn</code> procedure to add the standard metadata for a
 * geometry column.
 */
public class CreateSpatialTableGeneratorGeoDB extends AbstractSqlGenerator<CreateTableStatement> {
   /**
    * @see liquibase.sqlgenerator.core.AbstractSqlGenerator#supports(liquibase.statement.SqlStatement,
    *      liquibase.database.Database)
    */
   @Override
   public boolean supports(final CreateTableStatement statement, final Database database) {
      return database instanceof DerbyDatabase || database instanceof H2Database;
   }

   /**
    * @see liquibase.sqlgenerator.core.AbstractSqlGenerator#getPriority()
    */
   @Override
   public int getPriority() {
      return super.getPriority() + 1;
   }

   @Override
   public ValidationErrors validate(final CreateTableStatement statement, final Database database,
         final SqlGeneratorChain sqlGeneratorChain) {
      final ValidationErrors validationErrors = new ValidationErrors();
      for (final Entry<String, LiquibaseDataType> entry : statement.getColumnTypes().entrySet()) {
         if (entry.getValue() instanceof GeometryType) {
            final GeometryType geometryType = (GeometryType) entry.getValue();
            if (geometryType.getSRID() == null) {
               validationErrors.addError("The SRID parameter is required on the geometry type");
            }
         }
      }
      validationErrors.addAll(sqlGeneratorChain.validate(statement, database));
      return validationErrors;
   }

   @Override
   public Sql[] generateSql(final CreateTableStatement statement, final Database database,
         final SqlGeneratorChain sqlGeneratorChain) {
      final List<Sql> list = new ArrayList<Sql>(Arrays.asList(sqlGeneratorChain.generateSql(
            statement, database)));
      for (final Entry<String, LiquibaseDataType> entry : statement.getColumnTypes().entrySet()) {
         if (entry.getValue() instanceof GeometryType) {
            final String columnName = entry.getKey();
            final GeometryType geometryType = (GeometryType) entry.getValue();
            final AddGeometryColumnGeneratorGeoDB generator = new AddGeometryColumnGeneratorGeoDB();
            final AddColumnStatement addColumnStatement = new AddColumnStatement(
                  statement.getCatalogName(), statement.getSchemaName(), statement.getTableName(),
                  columnName, geometryType.toString(), null);
            @SuppressWarnings("rawtypes")
            final SqlGeneratorChain emptyChain = new SqlGeneratorChain(new TreeSet<SqlGenerator>());
            final Sql[] addGeometryColumnSql = generator.generateSql(addColumnStatement, database,
                  emptyChain);
            list.addAll(Arrays.asList(addGeometryColumnSql));
         }
      }
      return list.toArray(new Sql[list.size()]);
   }
}