package __BASE_PACKAGE__;

import java.io.File
import java.io.IOException

import org.knime.core.data.DataCell
import org.knime.core.data.DataColumnSpec
import org.knime.core.data.DataColumnSpecCreator
import org.knime.core.data.DataRow
import org.knime.core.data.DataTableSpec
import org.knime.core.data.RowKey
import org.knime.core.data.`def`.DefaultRow
import org.knime.core.data.`def`.DoubleCell
import org.knime.core.data.`def`.IntCell
import org.knime.core.data.`def`.StringCell
import org.knime.core.node.BufferedDataContainer
import org.knime.core.node.BufferedDataTable
import org.knime.core.node.CanceledExecutionException
import org.knime.core.node.ExecutionContext
import org.knime.core.node.ExecutionMonitor
import org.knime.core.node.InvalidSettingsException
import org.knime.core.node.NodeLogger
import org.knime.core.node.NodeModel
import org.knime.core.node.NodeSettingsRO
import org.knime.core.node.NodeSettingsWO
import org.knime.core.node.defaultnodesettings.SettingsModelIntegerBounded
import org.knime.workbench.scala.Implicits._

/**
 * Companion object for __NODE_NAME__NodeModel.
 */
object __NODE_NAME__NodeModel {

  // the logger instance
  private[__NODE_NAME__NodeModel] final val logger = NodeLogger.getLogger(classOf[__NODE_NAME__NodeModel])

  /**
   * the settings key which is used to retrieve and
   * store the settings (from the dialog or from a settings file)
   * (package visibility to be usable from the dialog).
   */
  private[__BASE_PACKAGE_LAST__] final val CFGKEY_COUNT = "Count"

  /** initial default count value. */
  private[__BASE_PACKAGE_LAST__] final val DEFAULT_COUNT = 100
}

/**
 * This is the model implementation of __NODE_NAME__.
 * 
 * __DESCRIPTION__
 *
 * @author __VENDOR_NAME__
 */
class __NODE_NAME__NodeModel extends NodeModel(1, 1) {
  import __NODE_NAME__NodeModel.logger

  // example value: the models count variable filled from the dialog 
  // and used in the models execution method. The default components of the
  // dialog work with "SettingsModels".
  private[this] final val count =
    new SettingsModelIntegerBounded(__NODE_NAME__NodeModel.CFGKEY_COUNT,
      __NODE_NAME__NodeModel.DEFAULT_COUNT,
      Integer.MIN_VALUE, Integer.MAX_VALUE);
  /**
   * {@inheritDoc}
   */
  @throws[Exception]
  protected override def execute(inData: Array[BufferedDataTable],
    exec: ExecutionContext): Array[BufferedDataTable] = {
    implicit val executionContext = exec

    // TODO do something here
    logger.info("Node Model Stub... this is not yet implemented !")

    // the data table spec of the single output table, 
    // the table will have three columns:
    val allColSpecs = new Array[DataColumnSpec](3)
    allColSpecs(0) =
      new DataColumnSpecCreator("Column 0", StringCell.TYPE)
    allColSpecs(1) =
      new DataColumnSpecCreator("Column 1", DoubleCell.TYPE)
    allColSpecs(2) =
      new DataColumnSpecCreator("Column 2", IntCell.TYPE)
    val outputSpec: DataTableSpec = allColSpecs
    // the execution context will provide us with storage capacity, in this
    // case a data container to which we will add rows sequentially
    // Note, this container can also handle arbitrary big data tables, it
    // will buffer to disc if necessary.
    val container: BufferedDataContainer = outputSpec
    // let's add m_count rows to it
    for (i <- 0 until count) {
      val key: RowKey = i
      // the cells of the current row, the types of the cells must match
      // the column spec (see above)
      val cells = new Array[DataCell](3)
      cells(0) = a"String_$i"
      cells(1) = 0.5 * i
      cells(2) = i
      val row: DataRow = key withData cells

      // check if the execution monitor was cancelled
      checkCancelled
      setProgress(i / count.toDouble,
        "Adding row " + i)
    }
    // once we are done, we close the container and return its table
    //container.close
    //val out = container.getTable
    Array[BufferedDataTable] { container }
  }

  /**
   * @inheritdoc
   */
  protected override def reset: Unit = {
    // TODO Code executed on reset.
    // Models build during execute are cleared here.
    // Also data handled in load/saveInternals will be erased here.
  }

  /**
   * @inheritdoc
   */
  @throws[InvalidSettingsException]
  protected override def configure(inSpecs: Array[DataTableSpec]): Array[DataTableSpec] = {

    // TODO: check if user settings are available, fit to the incoming
    // table structure, and the incoming types are feasible for the node
    // to execute. If the node can execute in its current state return
    // the spec of its output data table(s) (if you can, otherwise an array
    // with null elements), or throw an exception with a useful user message

    Array[DataTableSpec] { null }
  }

  /**
   * @inheritdoc
   */
  protected override def saveSettingsTo(settings: NodeSettingsWO): Unit = {

    // TODO save user settings to the config object.

    count.saveSettingsTo(settings)

  }

  /**
   * @inheritdoc
   */
  @throws[InvalidSettingsException]
  protected override def loadValidatedSettingsFrom(settings: NodeSettingsRO): Unit = {

    // TODO load (valid) settings from the config object.
    // It can be safely assumed that the settings are valided by the 
    // method below.

    count.loadSettingsFrom(settings)

  }

  /**
   * @inheritdoc
   */
  @throws[InvalidSettingsException]
  protected override def validateSettings(settings: NodeSettingsRO): Unit = {

    // TODO check if the settings could be applied to our model
    // e.g. if the count is in a certain range (which is ensured by the
    // SettingsModel).
    // Do not actually set any values of any member variables.

    count.validateSettings(settings)

  }

  /**
   * @inheritdoc
   */
  @throws[IOException]
  @throws[CanceledExecutionException]
  protected override def loadInternals(internDir: File,
    exec: ExecutionMonitor): Unit = {

    // TODO load internal data. 
    // Everything handed to output ports is loaded automatically (data
    // returned by the execute method, models loaded in loadModelContent,
    // and user settings set through loadSettingsFrom - is all taken care 
    // of). Load here only the other internals that need to be restored
    // (e.g. data used by the views).

  }

  /**
   * @inheritdoc
   */
  @throws[IOException]
  @throws[CanceledExecutionException]
  protected override def saveInternals(internDir: File,
    exec: ExecutionMonitor): Unit = {

    // TODO save internal models. 
    // Everything written to output ports is saved automatically (data
    // returned by the execute method, models saved in the saveModelContent,
    // and user settings saved through saveSettingsTo - is all taken care 
    // of). Save here only the other internals that need to be preserved
    // (e.g. data used by the views).

  }
}