/*
 * This file is part of the Poliqarp suite.
 * 
 * Copyright (C) 2004-2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file gpl.txt included in the packaging
 * of this file.  (See http://www.gnu.org/licenses/translations.html for
 * unofficial translations.)
 * 
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

#include <poliqarp-config.h>

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <foostring/foostring.h>
#include "viewport.h"

struct viewport *vp_out;
struct viewport *vp_err;

void vp_glob_init(void)
{
   vp_out = vp_new(stdout);
   vp_err = vp_new(stderr);
}

void vp_glob_free(void)
{
   vp_close(vp_out);
   vp_close(vp_err);
}

struct viewport *vp_new(FILE *stream)
{
   struct viewport *this = malloc (sizeof *this);
   if (this == NULL)
      return NULL;

   this->nesting = &this->local_nesting;
   this->stream.stream = stream;
   this->stream.close_on_exit = false;
   this->stream.prefix = NULL;
   this->local_nesting = 0;

   return this;
}

struct viewport *vp_new_file(const char *pathname, const char *mode)
{
   FILE *stream = fopen(pathname, mode);
   struct viewport *this;

   if (!stream)
      return NULL;
   this = vp_new(stream);
   this->stream.close_on_exit = true;
   return this;
}

void vp_close(struct viewport *this)
{
   free(this->stream.prefix);
   if (this->stream.close_on_exit)
      fclose(this->stream.stream);
   else
      fflush(this->stream.stream);
   free(this);
}

void vp_redirect(struct viewport *this, const char *pathname, const char *mode)
{
   FILE *stream = fopen (pathname, mode);

   if (!stream)
      return;
   this->stream.stream = stream;
   this->stream.close_on_exit = true;
}

void vp_flush(struct viewport *this)
{
   fflush(this->stream.stream);
}

void vp_nest(struct viewport *this)
{
   (*this->nesting)++;
}

void vp_unnest(struct viewport *this)
{
   assert(*this->nesting > 0);
   (*this->nesting)--;
}

void vp_prefix(struct viewport *this, const char *prefix)
{
   if (prefix == NULL) {
      free(this->stream.prefix);
      this->stream.prefix = NULL;
   } else if (this->stream.prefix != prefix) {
      free(this->stream.prefix);
      this->stream.prefix = strdup(prefix);
   }
}

int vp_fprintf(struct viewport *this, const char *format, ...)
{
   va_list ap;
   int n;

   va_start(ap, format);
   n = vp_vpfprintf(this, this->stream.prefix, format, ap);
   va_end(ap);
   return n;
}

int vp_pfprintf(struct viewport *this, const char *prefix, const char *format, 
   ...)
{
   va_list ap;
   int n;

   va_start(ap, format);
   n = vp_vpfprintf(this, prefix, format, ap);
   va_end(ap);
   return n;
}

int vp_vfprintf(struct viewport *this, const char *format, va_list ap)
{
   return vp_vpfprintf(this, this->stream.prefix, format, ap);
}

int vp_vpfprintf(struct viewport *this, const char *prefix, const char *format,
   va_list ap)
{
   int n = 0;
   unsigned int i;

   if (prefix)
      n += fprintf(this->stream.stream, "%s", prefix);
   for (i = 0; i < *(this->nesting); ++i)
      n += fprintf(this->stream.stream, "   ");
   n += vfprintf(this->stream.stream, format, ap);
   return n;
}
