/*-
 * Copyright (c) 2002-2018 The UbixOS Project.
 * All rights reserved.
 *
 * This was developed by Christopher W. Olsen for the UbixOS Project.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 * 1) Redistributions of source code must retain the above copyright notice, this list of
 *    conditions, the following disclaimer and the list of authors.
 * 2) Redistributions in binary form must reproduce the above copyright notice, this list of
 *    conditions, the following disclaimer and the list of authors in the documentation and/or
 *    other materials provided with the distribution.
 * 3) Neither the name of the UbixOS Project nor the names of its contributors may be used to
 *    endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <ubixos/tty.h>
#include <ubixos/kpanic.h>
#include <ubixos/spinlock.h>
#include <lib/kprintf.h>
#include <lib/kmalloc.h>
#include <sys/io.h>
#include <string.h>

static tty_term *terms = 0x0;
tty_term *tty_foreground = 0x0;
static struct spinLock tty_spinLock = SPIN_LOCK_INITIALIZER;

int tty_init() {
  int i = 0x0;

  /* Allocate memory for terminals */
  terms = (tty_term *) kmalloc(sizeof(tty_term) * TTY_MAX_TERMS);
  if (terms == 0x0)
    kpanic("tty_init: Failed to allocate memory. File: %s, Line: %i\n", __FILE__, __LINE__);

  /* Set up all default terminal information */
  for (i = 0; i < TTY_MAX_TERMS; i++) {
    terms[i].tty_buffer = (char *) kmalloc(80 * 60 * 2);
    if (terms[i].tty_buffer == 0x0)
      kpanic("tty_init: Failed to allocate buffer memory. File: %s, Line: %i\n", __FILE__, __LINE__);

    terms[i].tty_pointer = terms[i].tty_buffer; /* Set up tty pointer to internal buffer */
    terms[i].tty_x = 0x0; /* Set up default X position             */
    terms[i].tty_y = 0x0; /* Set up default Y position             */
    terms[i].tty_colour = 0x0A + i; /* Set up default tty text colour        */
  }

  /* Read tty0 current position (to migrate from kprintf). */
  outportByte(0x3D4, 0x0e);
  terms[0].tty_y = inportByte(0x3D5);
  outportByte(0x3D4, 0x0f);
  terms[0].tty_x = inportByte(0x3D5);

  /* Set up pointer for the foreground tty */
  tty_foreground = &terms[0];

  /* Set up the foreground ttys information */
  tty_foreground->tty_pointer = (char *) 0xB8000;

  /* Return to let kernel know initialization is complete */
  kprintf("tty0 - Initialized\n");

  return (0x0);
}

/*
 This will change the specified tty. It ultimately copies the screen
 to the foreground buffer copies the new ttys buffer to the screen and
 adjusts a couple pointers and we are good to go.
 */
int tty_change(uInt16 tty) {

  if (tty > TTY_MAX_TERMS)
    kpanic("Error: Changing to an invalid tty. File: %s, Line: %i\n", __FILE__, __LINE__);

  /* Copy display buffer to tty buffer */
  memcpy(tty_foreground->tty_buffer, (char *) 0xB8000, (80 * 60 * 2));

  /* Copy new tty buffer to display buffer */
  memcpy((char *) 0xB8000, terms[tty].tty_buffer, (80 * 60 * 2));

  /*
   Set the tty_pointer to the internal buffer so I can continue 
   writing to what it believes is the screen
   */
  tty_foreground->tty_pointer = tty_foreground->tty_buffer;

  terms[tty].tty_pointer = (char *) 0xB8000;

  /* set new foreground tty */
  tty_foreground = &terms[tty];

  /* Adjust cursor when we change consoles */
  outportByte(0x3D4, 0x0F);
  outportByte(0x3D5, tty_foreground->tty_x);
  outportByte(0x3D4, 0x0E);
  outportByte(0x3D5, tty_foreground->tty_y);

  return (0x0);
}

int tty_print(char *string, tty_term *term) {
  unsigned int bufferOffset = 0x0, character = 0x0, i = 0x0;
  spinLock(&tty_spinLock);

  /* We Need To Get The Y Position */
  bufferOffset = term->tty_y;
  bufferOffset <<= 8;

  /* Then We Need To Add The X Position */
  bufferOffset += term->tty_x;
  bufferOffset <<= 1;

  while ((character = *string++)) {
    switch (character) {
      case '\n':
        bufferOffset = (bufferOffset / 160) * 160 + 160;
      break;
      default:
        term->tty_pointer[bufferOffset++] = character;
        term->tty_pointer[bufferOffset++] = term->tty_colour;
      break;
    } /* switch */

    /* Check To See If We Are Out Of Bounds */
    if (bufferOffset >= 160 * 25) {
      for (i = 0; i < 160 * 24; i++) {
        term->tty_pointer[i] = term->tty_pointer[i + 160];
      }
      for (i = 0; i < 80; i++) {
        term->tty_pointer[(160 * 24) + (i * 2)] = 0x20;
        term->tty_pointer[(160 * 24) + (i * 2) + 1] = term->tty_colour;
      }
      bufferOffset -= 160;
    }
  }

  bufferOffset >>= 1; /* Set the new cursor position  */
  term->tty_x = (bufferOffset & 0xFF);
  term->tty_y = (bufferOffset >> 0x8);

  if (term == tty_foreground) {
    outportByte(0x3D4, 0x0f);
    outportByte(0x3D5, term->tty_x);
    outportByte(0x3D4, 0x0e);
    outportByte(0x3D5, term->tty_y);
  }

  spinUnlock(&tty_spinLock);

  return (0x0);
}

tty_term *tty_find(uInt16 tty) {
  return (&terms[tty]);
}

/***
 END
 ***/
