// log_select.C
// 
// Copyright 1996-2001, 2005 Per Abrahamsen and Søren Hansen
// Copyright 2000-2001, 2005 KVL.
//
// This file is part of Daisy.
// 
// Daisy is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
// 
// Daisy 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 Lesser Public License for more details.
// 
// You should have received a copy of the GNU Lesser Public License
// along with Daisy; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#define BUILD_DLL

#include "log_select.h"
#include "select.h"
#include "condition.h"
#include "metalib.h"
#include "library.h"
#include "block_top.h"
#include "block_model.h"
#include "field.h"
#include "format.h"
#include "volume.h"
#include "scope.h"
#include "assertion.h"
#include "memutils.h"
#include "librarian.h"
#include "treelog.h"
#include "frame_model.h"
#include "daisy.h"
#include <sstream>

bool 
LogSelect::check_leaf (symbol) const
{ daisy_notreached (); }

bool 
LogSelect::check_interior (symbol) const
{ daisy_notreached (); }

bool 
LogSelect::check_derived (symbol field, const symbol /* name */,
			  const symbol /* component */) const
{ return check_interior (field); }

bool 
LogSelect::match (const Daisy& daisy, Treelog& out)
{
  condition->tick (daisy, Scope::null (), out);
  is_printing = condition->match (daisy, Scope::null (), out);
  is_active = is_printing;

  for (std::vector<Select*>::const_iterator i = entries.begin (); 
       i < entries.end (); 
       i++)
    if ((*i)->match (is_printing))
      is_active = true;

  return is_active;
}

void
LogSelect::done (const std::vector<Time::component_t>& time_columns,
		 const Time&, const double dt, Treelog&)
{ 
  for (std::vector<Select*>::const_iterator i = entries.begin (); 
       i < entries.end (); 
       i++)
    (*i)->done_small (dt);
}

bool
LogSelect::initial_match (const Daisy& daisy, const Time& previous, Treelog&)
{
  condition->initiate_log (daisy, previous);

  is_active = false;

  for (std::vector<Select*>::const_iterator i = entries.begin (); 
       i < entries.end (); 
       i++)
    if ((*i)->initial_match ())
      is_active = true;

  is_printing = is_active;

  return is_active;
}

void
LogSelect::initial_done (const std::vector<Time::component_t>& time_columns,
			 const Time&, Treelog&)
{
  for (std::vector<Select*>::const_iterator i = entries.begin (); 
       i < entries.end (); 
       i++)
    (*i)->done_small (0.0);
}

void 
LogSelect::open_derived_type (const symbol type, const symbol)
{ open (type); }

void 
LogSelect::open (symbol)
{ daisy_notreached (); }

void 
LogSelect::close ()
{ daisy_notreached (); }

void 
LogSelect::open_unnamed ()
{ }

void 
LogSelect::close_unnamed ()
{ }

void 
LogSelect::open_named (const symbol name)
{ open (name); }

void 
LogSelect::close_named ()
{ close (); }

void 
LogSelect::open_ordered (const int index)
{ 
  open (symbol (index));
}

void 
LogSelect::close_ordered ()
{ close (); }

void 
LogSelect::open_derived (symbol field, symbol type, const symbol library)
{ open (field); open_derived_type (type, library); }

void 
LogSelect::close_derived ()
{ close (); close (); }

void 
LogSelect::open_object (const symbol field, const symbol type, const Frame&,
			const symbol library)
{ open_derived (field, type, library); }

void 
LogSelect::close_object ()
{ close_derived (); }

void 
LogSelect::open_entry (const symbol type, const Frame&, 
		       const symbol library)
{ open_derived_type (type, library); }

void 
LogSelect::close_entry ()
{ close (); }

void 
LogSelect::open_named_entry (symbol name, symbol, 
			     const Frame&)
{ open (name); }

void 
LogSelect::close_named_entry ()
{ close (); }

void 
LogSelect::open_shallow (symbol type, const symbol library)
{ open_derived_type (type, library); }

void 
LogSelect::close_shallow ()
{ close (); }

void 
LogSelect::output_entry (symbol, const bool)
{ daisy_notreached (); }

void 
LogSelect::output_entry (symbol, const double)
{ daisy_notreached (); }

void 
LogSelect::output_entry (symbol, const int)
{ daisy_notreached (); }

void 
LogSelect::output_entry (symbol, const symbol)
{ daisy_notreached (); }

void 
LogSelect::output_entry (symbol, const std::vector<double>&)
{ daisy_notreached (); }

void 
LogSelect::output_entry (symbol, const PLF&)
{ daisy_notreached (); }

bool 
LogSelect::check (const Border& border, Treelog& err) const
{
  bool ok = true;

  if (!volume->check_border (border, err))
    { /* ok = false */ }

  for (unsigned int i = 0; i < entries.size (); i++)
    {
      std::ostringstream tmp;
      tmp << "entries [" << i << "]: " << entries[i]->tag ();
      Treelog::Open nest (err, tmp.str ());
      if (!entries[i]->check (err))
        ok = false;
      if (!entries[i]->check_border (border, *volume, err))
        { /* ok = false */ }
    }
  return ok; 
}

LogSelect::LogSelect (const BlockModel& al)
  : Log (al),
    description (al.name ("description", "")),
    condition (Librarian::build_item<Condition> (al, "when")),
    entries (Librarian::build_vector<Select> (al, "entries")),
    volume (Volume::build_obsolete (al))
{
  if (!al.ok ())
    return;

  // Initialize entries.
  for (unsigned int i = 0; i < entries.size (); i++)
    if (!entries[i]->initialize (al.units (), *volume, condition->timestep (),
                                 al.msg ()))
      al.set_error ();
}

LogSelect::LogSelect (const char *const id)
  : Log (id),
    description ("Build in log select use."),
    condition (Condition::create_true ()),
    entries (std::vector<Select*> ()),
    volume (Volume::build_none ())
{ }

  
LogSelect::~LogSelect ()
{ }

void
LogSelect::document_entries (Format& format, const Metalib& metalib, 
                             Treelog& msg, const symbol name)
{
  const Library& log_lib = metalib.library (Log::component);
  const FrameModel& frame = log_lib.model (name);

  // We need a type.
  const symbol parent = frame.base_name ();
  if (parent == Attribute::None ())
    {
      msg.warning ("bug: Orphan log parameterisation.");
      return;
    }

  // Check if this log parameterizations adds something compared to
  // its parent. 
  daisy_assert (parent != name);
  if (log_lib.check (parent)
      && frame.subset (metalib, log_lib.model (parent), "entries"))
    // If not, don't document the entries.
    return;

  // Incomplete log.
  if (!frame.check (metalib, Treelog::null ()))
    {
      if (!frame.check ("entries"))
	return;

      const std::vector<boost::shared_ptr<const FrameModel>/**/>& entries 
        = frame.model_sequence ("entries");
      if (entries.size () < 1)
	return;

      // At least one interesting description required.
      const Library& library = metalib.library (Select::component);
      int interesting = 0;
      for (size_t i = 0; i < entries.size (); i++)
	if (library.has_interesting_description (*entries[i]))
	  interesting++;
      if (interesting < 1)
	return;

      format.bold ("Table columns include:");
      Format::List dummy (format);

      for (size_t i = 0; i < entries.size (); i++)
	{
	  const Frame& entry = *entries[i];
	  Format::Item d2 (format, Select::select_get_tag (entry).name ());
	  if (library.has_interesting_description (entry))
	    format.text (entry.description ());
	  format.soft_linebreak ();
	}
      return;
    }

  // Complete log.
  BlockTop block (metalib, msg, frame);
  auto_vector<Select*> entries (Librarian::build_vector<Select> (block,
                                                                 "entries")); 
  daisy_assert (block.ok ());

  format.bold ("Table columns:");
  Format::List dummy (format);

  for (size_t i = 0; i < entries.size (); i++)
    entries[i]->document (format);
}

static struct LogSelectSyntax : public DeclareBase
{
  LogSelectSyntax ()
    : DeclareBase (Log::component, "select", "Select variables to log.")
  { }
  void load_frame (Frame& frame) const
  {
    Model::load_model (frame);
    frame.declare_object ("when", Condition::component, "\
Add entries to the log file when this condition is true.");
    frame.declare_object ("entries", Select::component, 
                       Attribute::State, Attribute::Variable,
                       "What to log in each column.");
    frame.declare_boolean ("time_columns", Attribute::OptionalConst, "\
Iff true, add columns for year, month, mday and hour in the begining of\n\
the lines.  By default, this will be true of you have not specified any\n\
time entries yourself.");
    frame.declare_object ("volume", Volume::component, 
                       Attribute::Const, Attribute::Singleton,
                       "Soil volume to log.");
    frame.set ("volume", "box");
    frame.declare ("from", "cm", Attribute::OptionalConst,
                "Default 'from' value for all entries.\n\
By default, use the top of the soil.\n\
OBSOLETE: Use (volume box (top FROM)) instead.");
    frame.declare ("to", "cm", Attribute::OptionalConst,
                "Default 'to' value for all entries.\n\
By default, use the bottom of the soil.\n\
OBSOLETE: Use (volume box (bottom TO)) instead.");
  }
} LogSelect_syntax;

// log_select.C ends here.
