/*
Copyright (C) 2013-2014 Draios inc.

This file is part of sysdig.

sysdig is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

sysdig 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 sysdig.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include "sinsp.h"
#include "sinsp_int.h"
#include "sinsp_errno.h"
#include "sinsp_signal.h"
#include "filter.h"
#include "filterchecks.h"
#include "chisel.h"
#include "protodecoder.h"

///////////////////////////////////////////////////////////////////////////////
// sinsp_view_column_info implementation
///////////////////////////////////////////////////////////////////////////////
string sinsp_view_column_info::get_field(uint32_t depth)
{
	// Trim the string
	replace_in_place(m_field, " ", "");
	replace_in_place(m_field, "\t", "");

	if(m_field.find("%depth-1") != string::npos)
	{
		string res = m_field;
		replace_in_place(res, "%depth-1", to_string(depth - 1));
		return res;
	}
	else if(m_field.find("%depth+1") != string::npos)
	{
		string res = m_field;
		replace_in_place(res, "%depth+1", to_string(depth - 1));
		return res;
	}
	else if(m_field.find("%depth") != string::npos)
	{
		string res = m_field;
		replace_in_place(res, "%depth", to_string(depth));
		return res;
	}
	else
	{
		return m_field;
	}
}

string sinsp_view_column_info::get_filter_field(uint32_t depth)
{
	//
	// If m_filterfield, return it as an override to m_field
	//
	if(m_filterfield != "")
	{
		return m_filterfield;
	}
	else
	{
		return get_field(depth);
	}
}

///////////////////////////////////////////////////////////////////////////////
// sinsp_view_info implementation
///////////////////////////////////////////////////////////////////////////////
sinsp_view_info::sinsp_view_info()
{
	m_valid = false;
}

sinsp_view_info::sinsp_view_info(viewtype type, 
	string id,
	string name,
	string description,
	vector<string> tags,
	vector<string> tips,
	vector<sinsp_view_column_info> columns,
	vector<string> applies_to,
	string filter,
	string drilldown_target,
	bool use_defaults,
	bool is_root,
	vector<sinsp_view_action_info> actions,
	bool drilldown_increase_depth,
	string spectro_type,
	bool propagate_filter)
{
	m_id = id;
	m_name = name;
	m_description = description;
	m_does_groupby = false;
	m_type = type;
	m_tags = tags;	
	m_tips = tips;
	m_columns = columns;
	m_drilldown_target = drilldown_target;
	m_is_root = is_root;
	m_applies_to = applies_to;
	m_drilldown_increase_depth = drilldown_increase_depth;
	m_spectro_type = spectro_type;
	m_propagate_filter = propagate_filter;

	m_use_defaults = use_defaults;
	
	//
	// Make sure the keys go at the beginning
	//
	move_key_to_front(TEF_IS_GROUPBY_KEY);
	move_key_to_front(TEF_IS_KEY);

	//
	// Determine the sorting and grouping columns
	//
	set_sorting_col();

	m_filter = filter;
	m_valid = true;
	m_actions = actions;
	// init the array for hotkeys for sorting columns
	set_col_sorting_hotkeys();
}

void sinsp_view_info::set_col_sorting_hotkeys()
{
	const char shift_number_keys [] = {'!', '@', '#', '$', '%', '^', '&', '*', '('};
	uint32_t size = sizeof(shift_number_keys) / sizeof(shift_number_keys[0]);
	for(uint32_t i=0; i<size; i++) 
	{
		m_col_sort_hotkeys.push_back(shift_number_keys[i]); 
	}
	max_col_sort_hotkeys = m_col_sort_hotkeys.size();
}

void sinsp_view_info::set_sorting_col()
{
	m_n_sorting_cols = 0;

	for(uint32_t j = 0; j < m_columns.size(); j++)
	{
		if((m_columns[j].m_flags & TEF_IS_SORT_COLUMN) != 0)
		{
			m_sortingcol = j;
			m_n_sorting_cols++;
		}

		if((m_columns[j].m_flags & TEF_IS_GROUPBY_KEY) != 0)
		{
			m_does_groupby = true;
		}
	}

	if(m_does_groupby)
	{
		m_sortingcol--;
	}

	if(m_n_sorting_cols == 0)
	{
		if(m_does_groupby)
		{
			m_sortingcol = 2;
		}
		else
		{
			m_sortingcol = 1;
		}
	}
	else if(m_n_sorting_cols > 1)
	{
		throw sinsp_exception("view format error: more than one sorting column");
	}

	if((int64_t)m_sortingcol < 0)
	{
		ASSERT(false);
		throw sinsp_exception("view sorting column configuration error");
	}
}

void sinsp_view_info::apply_tag(string tag)
{
	for(auto it = m_columns.begin(); it != m_columns.end();)
	{
		bool found = false;

		if(it->m_tags.size() != 0)
		{
			for(string t : it->m_tags)
			{
				if(t == tag)
				{
					found = true;
					break;
				}
			}

			if(!found)
			{
				it = m_columns.erase(it);
				continue;
			}
		}

		++it;
	}

	//
	// Make sure to recalculate the sorting and grouping columns, which could change
	// if we remove columns.
	//
	set_sorting_col();
}

void sinsp_view_info::get_col_names_and_sizes(OUT vector<string>* colnames, OUT vector<int32_t>* colsizes)
{
	if(m_type == viewtype::T_LIST)
	{
		colsizes->push_back(-1);
		colnames->push_back("");
	}

	for(auto fit : m_columns)
	{
		if(m_does_groupby)
		{
			if((fit.m_flags & TEF_IS_KEY) != 0)
			{
				continue;
			}

			if((fit.m_flags & TEF_IS_GROUPBY_KEY) != 0)
			{
				colsizes->insert(colsizes->begin(), fit.m_colsize);
				colnames->insert(colnames->begin(), fit.m_name);
				continue;
			}
		}

		colsizes->push_back(fit.m_colsize);
		colnames->push_back(fit.m_name);
	}
}

void sinsp_view_info::move_key_to_front(uint32_t keyflag)
{
	for(uint32_t j = 0; j < m_columns.size(); j++)
	{
		if((m_columns[j].m_flags & keyflag) != 0)
		{
			sinsp_view_column_info ci = m_columns[j];

			m_columns.erase(m_columns.begin() +j);
			m_columns.insert(m_columns.begin(), ci);
			return;
		}
	}
}

sinsp_view_column_info* sinsp_view_info::get_key()
{
	for(uint32_t j = 0; j < m_columns.size(); j++)
	{
		if((m_columns[j].m_flags & TEF_IS_GROUPBY_KEY) != 0)
		{
			return &m_columns[j];
		}
	}

	for(uint32_t j = 0; j < m_columns.size(); j++)
	{
		if((m_columns[j].m_flags & TEF_IS_KEY) != 0)
		{
			return &m_columns[j];
		}
	}

	// The *must* be a key
	return NULL;
}

string sinsp_view_info::get_filter(uint32_t depth)
{
	if(m_filter.find("%depth+1") != string::npos)
	{
		string res = m_filter;
		replace_in_place(res, "%depth+1", to_string(depth + 1));
		replace_in_place(res, "%depth + 1", to_string(depth + 1));
		return res;
	}
	else if(m_filter.find("%depth-1") != string::npos)
	{
		string res = m_filter;
		replace_in_place(res, "%depth-1", to_string(depth - 1));
		replace_in_place(res, "%depth - 1", to_string(depth - 1));
		return res;
	}
	else if(m_filter.find("%depth") != string::npos)
	{
		string res = m_filter;
		replace_in_place(res, "%depth", to_string(depth));
		return res;
	}
	else
	{
		return m_filter;
	}
}

///////////////////////////////////////////////////////////////////////////////
// sinsp_view_manager implementation
///////////////////////////////////////////////////////////////////////////////
void sinsp_view_manager::add(sinsp_view_info* vinfo)
{
	m_views.push_back(*vinfo);
}

typedef struct view_cmp
{
	bool operator()(const sinsp_view_info& src, const sinsp_view_info& dst)
	{
		return src.m_name < dst.m_name;
	}
}table_row_cmp;

void sinsp_view_manager::sort_views()
{
	view_cmp cc;

	//
	// Sort the list alphabetically
	//
	sort(m_views.begin(),
		m_views.end(),
		cc);

	//
	// Print the view list for debugging purposes
	//
	//for(uint32_t j = 0; j < m_views.size(); j++)
	//{
	//	g_logger.format("> %d) %s", j, m_views[j].m_name.c_str());
	//}
}

vector<sinsp_view_info>* sinsp_view_manager::get_views()
{
	sort_views();
	return &m_views;
}

uint32_t sinsp_view_manager::get_selected_view()
{
	sort_views();

	if(m_selected_view_id != "")
	{
		for(uint32_t j = 0; j < m_views.size(); j++)
		{
			if(m_views[j].m_id == m_selected_view_id)
			{
				return j;
			}
		}

		if(m_selected_view_id == "echo")
		{
			return VIEW_ID_SPY;
		}
		else if(m_selected_view_id == "dig")
		{
			return VIEW_ID_DIG;
		}
	}
	else
	{
		for(uint32_t j = 0; j < m_views.size(); j++)
		{
			if(m_views[j].m_is_root)
			{
				return j;
			}
		}
	}

	throw sinsp_exception("view " + m_selected_view_id + " not found");
	return 0;
}

void sinsp_view_manager::set_selected_view(string viewid)
{
	m_selected_view_id = viewid;
}
