/*
 * Copyright 2010-2012 Luca Garulli (l.garulli--at--orientechnologies.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.orientechnologies.orient.server.network.protocol.http.command.post;

import java.io.BufferedReader;
import java.io.StringReader;
import java.text.NumberFormat;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Locale;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.serialization.serializer.OStringSerializerHelper;
import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerCSVAbstract;
import com.orientechnologies.orient.server.network.protocol.http.OHttpRequest;
import com.orientechnologies.orient.server.network.protocol.http.OHttpResponse;
import com.orientechnologies.orient.server.network.protocol.http.OHttpUtils;
import com.orientechnologies.orient.server.network.protocol.http.command.OServerCommandDocumentAbstract;

public class OServerCommandPostImportRecords extends OServerCommandDocumentAbstract {
  private static final char     CSV_SEPARATOR     = ',';
  private static final char     CSV_STR_DELIMITER = '"';

  private static final String[] NAMES             = { "POST|importRecords/*" };

  @Override
  public boolean execute(final OHttpRequest iRequest, OHttpResponse iResponse) throws Exception {
    final String[] urlParts = checkSyntax(iRequest.url, 4,
        "Syntax error: importRecords/<database>/<format>/<class>[/<separator>][/<string-delimiter>][/<locale>]");

    final long start = System.currentTimeMillis();

    iRequest.data.commandInfo = "Import records";

    ODatabaseDocumentTx db = getProfiledDatabaseInstance(iRequest);
    try {

      final OClass cls = db.getMetadata().getSchema().getClass(urlParts[3]);
      if (cls == null)
        throw new IllegalArgumentException("Class '" + urlParts[3] + " is not defined");

      if (iRequest.content == null)
        throw new IllegalArgumentException("Empty content");

      if (urlParts[2].equalsIgnoreCase("csv")) {
        final char separator = urlParts.length > 4 ? urlParts[4].charAt(0) : CSV_SEPARATOR;
        final char stringDelimiter = urlParts.length > 5 ? urlParts[5].charAt(0) : CSV_STR_DELIMITER;
        final Locale locale = urlParts.length > 6 ? new Locale(urlParts[6]) : Locale.getDefault();

        final BufferedReader reader = new BufferedReader(new StringReader(iRequest.content));
        final String header = reader.readLine().trim();
        if (header.length() == 0)
          throw new InputMismatchException("Missing CSV file header");

        final List<String> columns = OStringSerializerHelper.smartSplit(header, separator);
        for (int i = 0; i < columns.size(); ++i)
          columns.set(i, OStringSerializerHelper.getStringContent(columns.get(i)));

        int imported = 0;
        int errors = 0;

        final StringBuilder output = new StringBuilder();

        int line = 0;
        int col = 0;
        String column = "?";
        String parsedCell = "?";
        final NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);

        for (line = 2; reader.ready(); line++) {
          try {
            final String parsedRow = reader.readLine();
            if (parsedRow == null)
              break;

            final ODocument doc = new ODocument(cls);
            final String row = parsedRow.trim();
            final List<String> cells = OStringSerializerHelper.smartSplit(row, CSV_SEPARATOR);

            for (col = 0; col < columns.size(); ++col) {
              parsedCell = cells.get(col);
              column = columns.get(col);

              String cellValue = parsedCell.trim();

              if (cellValue.length() == 0 || cellValue.equalsIgnoreCase("null"))
                continue;

              Object value;
              if (cellValue.length() >= 2 && cellValue.charAt(0) == stringDelimiter
                  && cellValue.charAt(cellValue.length() - 1) == stringDelimiter)
                value = OStringSerializerHelper.getStringContent(cellValue);
              else {
                try {
                  value = numberFormat.parse(cellValue);
                } catch (Exception e) {
                  value = ORecordSerializerCSVAbstract.getTypeValue(cellValue);
                }
              }

              doc.field(columns.get(col), value);
            }

            doc.save();
            imported++;

          } catch (Exception e) {
            errors++;
            output.append(String.format("#%d: line %d column %s (%d) value '%s': '%s'\n", errors, line, column, col, parsedCell,
                e.toString()));
          }
        }

        final float elapsed = (System.currentTimeMillis() - start) / 1000;

        String message = String
            .format(
                "Import of records of class '%s' completed in %5.3f seconds. Line parsed: %d, imported: %d, error: %d\nDetailed messages:\n%s",
                cls.getName(), elapsed, line, imported, errors, output);

        iResponse.send(OHttpUtils.STATUS_CREATED_CODE, OHttpUtils.STATUS_CREATED_DESCRIPTION, OHttpUtils.CONTENT_TEXT_PLAIN,
            message, null);
        return false;

      } else
        throw new UnsupportedOperationException("Unsupported format on importing record. Available formats are: csv");

    } finally {
      if (db != null)
        db.close();
    }
  }

  @Override
  public String[] getNames() {
    return NAMES;
  }
}
