/*
    cssh - cluster secure shell
    Copyright (C) 2015-2021  Andreas Baumann

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "progressbar.h"

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <termcap.h>

int create_progressbar( cssh_progressbar_t *progressbar, uint64_t min_value, uint64_t max_value, size_t size, const char *label_fmt, ... )
{
	progressbar->min_value = min_value;
	progressbar->max_value = max_value;

	va_list va;
	va_start( va, label_fmt );
	
	progressbar->label = (char *)malloc( size );
	if( progressbar->label == NULL ) {
		return -1;
	}

	(void)vsnprintf( progressbar->label, size, label_fmt, va );
	
	progressbar->value = progressbar->min_value;
	progressbar->total_steps = 0;
	progressbar->current_step = 0;
	progressbar->buf = NULL;
	
	va_end( va );
	
	return 0;
}

void free_progressbar( cssh_progressbar_t *progressbar )
{
	free( progressbar->buf );
	progressbar->buf = NULL;
	free( progressbar->label );
	progressbar->label = NULL;
	progressbar->value = progressbar->min_value;
	progressbar->total_steps = 0;
	progressbar->current_step = 0;
}

void set_value_of_progressbar( cssh_progressbar_t *progressbar, uint64_t value )
{
	progressbar->value = value;
}

static int compute_progressbar_steps( cssh_progressbar_t *progressbar )
{
	cssh_progressbar_pool_t *pool = progressbar->pool;
	
	if( strlen( progressbar->label ) + 2 < pool->cols ) {
		progressbar->total_steps = pool->cols - strlen( progressbar->label ) - 2;
	} else {
		progressbar->total_steps = 0;
	}
	progressbar->buf = (char *)malloc( progressbar->total_steps + 1 );
	if( progressbar->buf == NULL ) {
		return -1;
	}
	
	return 0;	
}

void redraw_progressbar( cssh_progressbar_t *progressbar )
{
	if( progressbar->total_steps == 0 ) {
		(void)compute_progressbar_steps( progressbar );
	}

	unsigned int new_step = progressbar->value * ( (double)progressbar->total_steps / ( progressbar->max_value - progressbar->min_value ) );
	if( new_step == progressbar->current_step ) {
		printf( "\n" );
		return;
	}
		
	progressbar->buf[progressbar->total_steps] = '\0';
	memset( progressbar->buf, (int)' ', progressbar->total_steps );
	memset( progressbar->buf, (int)'#', progressbar->current_step );

	fprintf( stderr, "%s [%s]\n", progressbar->label, progressbar->buf );

	progressbar->current_step = new_step;
}

int create_progressbar_pool( cssh_progressbar_pool_t *pool, size_t initial_size )
{
	pool->N = 0;
	pool->capacity = initial_size;
	pool->progressbar = (cssh_progressbar_t **)malloc( pool->capacity * sizeof( cssh_progressbar_t * ) );
	if( pool->progressbar == NULL ) {
		return -1;
	}

	pool->termbuf = (char *)malloc( TERMBUF_SIZE );
	if( pool->termbuf == NULL ) {
		free( pool->progressbar );
		return -1;
	}
	
	char *termtype = getenv( "TERM" );
	if( termtype == NULL ) {
		pool->cols = DEFAULT_COLS;
	}
	
	int rc = tgetent( pool->termbuf, termtype );
	
	if( rc < 0 ) {
		pool->cols = DEFAULT_COLS;
	} else {
		pool->cols = tgetnum( "co" ) - 2;
	}
	
	return 0;
}

void free_progressbar_pool( cssh_progressbar_pool_t *pool )
{
	for( size_t i = 0; i < pool->N; i++ ) {
		free_progressbar( pool->progressbar[i] );
	}
	
	free( pool->progressbar );
	free( pool->termbuf );
}

int append_progressbar_to_pool( cssh_progressbar_pool_t *pool, cssh_progressbar_t *progressbar )
{
	if( pool->N + 1 > pool->capacity ) {
		return -1;
	}
	
	pool->progressbar[pool->N] = progressbar;
	pool->N++;
	
	progressbar->pool = pool;
	
	return 0;
}

int remove_progressbar_from_pool( cssh_progressbar_pool_t *pool, cssh_progressbar_t *progressbar )
{
	if( pool->N == 0 ) {
		return -1;
	}
	
	for( size_t i = 0; i < pool->N; i++ ) {
		if( pool->progressbar[i] == progressbar ) {
			for( size_t j = i; j < pool->N - 1; j++ ) {
				pool->progressbar[j] = pool->progressbar[j+1];
			}
			pool->N--;
			pool->progressbar[pool->N] = NULL;
			return 0;
		}
	}
	
	return -1;
}

void redraw_progressbars( cssh_progressbar_pool_t *pool )
{
	
	for( size_t i = 0; i < pool->N; i++ ) {
		redraw_progressbar( pool->progressbar[i] );
	}
	printf( "\33[%zuF\n", pool->N + 1 );
}

