/*
htop - OptionItem.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "OptionItem.h"

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

#include "CRT.h"
#include "Macros.h"
#include "RichString.h"
#include "XUtils.h"


static void OptionItem_delete(Object* cast) {
   OptionItem* this = (OptionItem*)cast;
   assert (this != NULL);

   free(this->text);
   free(this);
}

static void TextItem_display(const Object* cast, RichString* out) {
   const TextItem* this = (const TextItem*)cast;
   assert (this != NULL);

   RichString_appendWide(out, CRT_colors[HELP_BOLD], this->super.text);
}

static void CheckItem_display(const Object* cast, RichString* out) {
   const CheckItem* this = (const CheckItem*)cast;
   assert (this != NULL);

   RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
   if (CheckItem_get(this)) {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
   } else {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
   }
   RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]    ");
   RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
}

static void NumberItem_display(const Object* cast, RichString* out) {
   const NumberItem* this = (const NumberItem*)cast;
   assert (this != NULL);

   char buffer[12];
   RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
   int written;
   if (this->scale < 0) {
      written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
   } else if (this->scale > 0) {
      written = xSnprintf(buffer, sizeof(buffer), "%d", (int) (pow(10, this->scale) * NumberItem_get(this)));
   } else {
      written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
   }
   RichString_appendnAscii(out, CRT_colors[CHECK_MARK], buffer, written);
   RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
   for (int i = written; i < 5; i++) {
      RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
   }
   RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
}

const OptionItemClass OptionItem_class = {
   .super = {
      .extends = Class(Object),
      .delete = OptionItem_delete
   }
};

const OptionItemClass TextItem_class = {
   .super = {
      .extends = Class(OptionItem),
      .delete = OptionItem_delete,
      .display = TextItem_display
   },
   .kind = OPTION_ITEM_TEXT
};


const OptionItemClass CheckItem_class = {
   .super = {
      .extends = Class(OptionItem),
      .delete = OptionItem_delete,
      .display = CheckItem_display
   },
   .kind = OPTION_ITEM_CHECK
};


const OptionItemClass NumberItem_class = {
   .super = {
      .extends = Class(OptionItem),
      .delete = OptionItem_delete,
      .display = NumberItem_display
   },
   .kind = OPTION_ITEM_NUMBER
};

TextItem* TextItem_new(const char* text) {
   TextItem* this = AllocThis(TextItem);
   this->super.text = xStrdup(text);
   return this;
}

CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
   CheckItem* this = AllocThis(CheckItem);
   this->super.text = xStrdup(text);
   this->value = false;
   this->ref = ref;
   return this;
}

CheckItem* CheckItem_newByVal(const char* text, bool value) {
   CheckItem* this = AllocThis(CheckItem);
   this->super.text = xStrdup(text);
   this->value = value;
   this->ref = NULL;
   return this;
}

bool CheckItem_get(const CheckItem* this) {
   if (this->ref) {
      return *(this->ref);
   } else {
      return this->value;
   }
}

void CheckItem_set(CheckItem* this, bool value) {
   if (this->ref) {
      *(this->ref) = value;
   } else {
      this->value = value;
   }
}

void CheckItem_toggle(CheckItem* this) {
   if (this->ref) {
      *(this->ref) = !*(this->ref);
   } else {
      this->value = !this->value;
   }
}

NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max) {
   assert(min <= max);

   NumberItem* this = AllocThis(NumberItem);
   this->super.text = xStrdup(text);
   this->value = 0;
   this->ref = ref;
   this->scale = scale;
   this->min = min;
   this->max = max;
   return this;
}

NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max) {
   assert(min <= max);

   NumberItem* this = AllocThis(NumberItem);
   this->super.text = xStrdup(text);
   this->value = CLAMP(value, min, max);
   this->ref = NULL;
   this->scale = scale;
   this->min = min;
   this->max = max;
   return this;
}

int NumberItem_get(const NumberItem* this) {
   if (this->ref) {
      return *(this->ref);
   } else {
      return this->value;
   }
}

void NumberItem_decrease(NumberItem* this) {
   if (this->ref) {
      *(this->ref) = CLAMP(*(this->ref) - 1, this->min, this->max);
   } else {
      this->value = CLAMP(this->value - 1, this->min, this->max);
   }
}

void NumberItem_increase(NumberItem* this) {
   if (this->ref) {
      *(this->ref) = CLAMP(*(this->ref) + 1, this->min, this->max);
   } else {
      this->value = CLAMP(this->value + 1, this->min, this->max);
   }
}

void NumberItem_toggle(NumberItem* this) {
   if (this->ref) {
      if (*(this->ref) >= this->max)
         *(this->ref) = this->min;
      else
         *(this->ref) += 1;
   } else {
      if (this->value >= this->max)
         this->value = this->min;
      else
         this->value += 1;
   }
}
