/*
** ZABBIX
** Copyright (C) 2000-2005 SIA Zabbix
**
** 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 2 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, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**/


#include "common.h"

#include "db.h"
#include "log.h"
#include "zlog.h"

#define ZBX_MAX_APPLICATIONS 64
#define ZBX_MAX_DEPENDENCES	128


#define	DBadd_application(name, hostid, templateid)	DBdb_save_application(name, hostid, 0, templateid)
#define DBupdate_application(applicationid, name, hostid, templateid)	DBdb_save_application(name, hostid, applicationid, templateid)

/******************************************************************************
 *                                                                            *
 * Function: DBget_applications_by_itemid                                     *
 *                                                                            *
 * Purpose: retrive applications by itemid                                    *
 *                                                                            *
 * Parameters: itemid - item identificator from database                      *
 *             applications - result buffer                                   *
 *             max_applications - size of result buffer in counts             *
 *                                                                            *
 * Return value: count of results                                             *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBget_applications_by_itemid(
		zbx_uint64_t	itemid,
		zbx_uint64_t	*applications,
		int		max_applications
	)
{
	register int	i = 0;

	DB_RESULT	db_applications;

	DB_ROW		application_data;

	db_applications = DBselect("select distinct applicationid from items_applications "
			" where itemid=" ZBX_FS_UI64, itemid);

	while( i < (max_applications - 1) && (application_data = DBfetch(db_applications)) )
	{
		ZBX_STR2UINT64(applications[i], application_data[0]);
		i++;
	}

	DBfree_result(db_applications);

	applications[i] = 0;

	return i;
}

/******************************************************************************
 *                                                                            *
 * Function: DBget_same_applications_for_host                                 *
 *                                                                            *
 * Purpose: retrive same applications for specified host                      *
 *                                                                            *
 * Parameters:  applications_in - zero terminated list of applicationid's     *
 *              hostid - host identificator from database                     *
 *              applications - result buffer                                  *
 *              max_applications - size of result buffer in count             *
 *                                                                            *
 * Return value: count of results                                             *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBget_same_applications_for_host(
		zbx_uint64_t    *applications_in,
		zbx_uint64_t	hostid,
		zbx_uint64_t	*applications,
		int		max_applications
	)
{
	register int	i = 0, j = 0;

	DB_RESULT	db_applications;

	DB_ROW		application_data;

	while( 0 < applications_in[i] && j < (max_applications - 1) )
	{
		db_applications = DBselect("select a1.applicationid from applications a1, applications a2"
				" where a1.name=a2.name and a1.hostid=" ZBX_FS_UI64 " and a2.applicationid=" ZBX_FS_UI64,
					hostid, applications_in[i]);

		if( (application_data = DBfetch(db_applications)) )
		{
			ZBX_STR2UINT64(applications[j], application_data[0]);
			j++;
		}

		DBfree_result(db_applications);
		i++;
	}

	applications[j] = 0;

	return j;
}

/******************************************************************************
 *                                                                            *
 * Function: DBinsert_dependency                                              *
 *                                                                            *
 * Purpose: create dependences for triggers                                   *
 *                                                                            *
 * Parameters: triggerid_down - down trigger identificator from database      *
 *             triggerid_up - up trigger identificator from database          *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBinsert_dependency(
		zbx_uint64_t triggerid_down,
		zbx_uint64_t triggerid_up
	)
{
	DBexecute("insert into trigger_depends (triggerdepid,triggerid_down,triggerid_up)"
			" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
			DBget_maxid("trigger_depends","triggerdepid"), triggerid_down, triggerid_up);

	DBexecute("update triggers set dep_level=dep_level+1 where triggerid=" ZBX_FS_UI64, triggerid_up);

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_dependencies_by_triggerid                               *
 *                                                                            *
 * Purpose: delete dependences from triggers                                  *
 *                                                                            *
 * Parameters: triggerid - trigger identificator from database                *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_dependencies_by_triggerid(
	zbx_uint64_t triggerid
	)
{
	DB_RESULT	db_trig_deps;

	DB_ROW		trig_dep_data;

	zbx_uint64_t
		triggerid_down,
		triggerid_up;

	db_trig_deps = DBselect("select triggerid_up, triggerid_down from trigger_depends where triggerid_down=" ZBX_FS_UI64, triggerid);

	while( (trig_dep_data = DBfetch(db_trig_deps)) )
	{
		ZBX_STR2UINT64(triggerid_up,	trig_dep_data[0]);
		ZBX_STR2UINT64(triggerid_down,	trig_dep_data[1]);

		DBexecute("update triggers set dep_level=dep_level-1 where triggerid=" ZBX_FS_UI64, triggerid_up);
		DBexecute("delete from trigger_depends where triggerid_up=" ZBX_FS_UI64 " and triggerid_down=" ZBX_FS_UI64,
						triggerid_up, triggerid_down);
	}

	DBfree_result(db_trig_deps);

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBclear_parents_from_trigger                                     *
 *                                                                            *
 * Purpose: removes any links between trigger and service if service          *
 *          is not leaf (treenode)                                            *
 *                                                                            *
 * Parameters: serviceid - id of service                                      *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static void	DBclear_parents_from_trigger(
		zbx_uint64_t serviceid
	)
{
	DB_RESULT res;
	DB_ROW rows;

	if( serviceid != 0 )
	{
		DBexecute("UPDATE services as s "
				" SET s.triggerid = null "
				" WHERE s.serviceid = " ZBX_FS_UI64, serviceid);
	}
	else
	{

		res = DBselect("SELECT s.serviceid "
					" FROM services as s, services_links as sl "
					" WHERE s.serviceid = sl.serviceupid "
					" AND NOT(s.triggerid IS NULL) "
					" GROUP BY s.serviceid");
		while( (rows = DBfetch(res)) )
		{
			ZBX_STR2UINT64(serviceid, rows[0]);

			DBexecute("UPDATE services as s "
					" SET s.triggerid = null "
					" WHERE s.serviceid = " ZBX_FS_UI64, serviceid);
		}
	}
}

/******************************************************************************
 *                                                                            *
 * Function: DBget_service_status                                             *
 *                                                                            *
 * Purpose: retrive true status                                               *
 *                                                                            *
 * Parameters:                                                                *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBget_service_status(
		zbx_uint64_t	serviceid,
		int		algorithm,
		zbx_uint64_t	triggerid
	)
{
	DB_RESULT	result;
	DB_ROW		row;

	int status = 0;
	char sort_order[MAX_STRING_LEN];
	char sql[MAX_STRING_LEN];
	
	if( 0 != triggerid )
	{
		result = DBselect("select priority from triggers where triggerid=" ZBX_FS_UI64 " and status=0 and value=%d", triggerid, TRIGGER_VALUE_TRUE);
		row = DBfetch(result);
		if(row && (DBis_null(row[0])!=SUCCEED))
		{
			status = atoi(row[0]);
		}
		DBfree_result(result);
	}

	if((SERVICE_ALGORITHM_MAX == algorithm) || (SERVICE_ALGORITHM_MIN == algorithm))
	{

		strcpy(sort_order,((SERVICE_ALGORITHM_MAX == algorithm)?" DESC ":" ASC "));
		
		zbx_snprintf(sql,sizeof(sql),"select s.status from services s,services_links l where l.serviceupid=" ZBX_FS_UI64 " and s.serviceid=l.servicedownid order by s.status %s", 
			serviceid, 
			sort_order);

		result = DBselectN(sql,1);

		row=DBfetch(result);
		if(row && DBis_null(row[0]) != SUCCEED)
		{
			if(atoi(row[0])!=0)
			{
				status = atoi(row[0]);
			}
		}
		DBfree_result(result);
	}
	return status;
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_services_rec                                            *
 *                                                                            *
 * Purpose: re-calculate and updates status of the service and its childs     *
 *                                                                            *
 * Parameters: serviceid - item to update services for                        *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Alexei Vladishev                                                   *
 *                                                                            *
 * Comments: recursive function                                               *
 *           !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
void	DBupdate_services_rec(
		zbx_uint64_t serviceid
	)
{
	int	status;
	zbx_uint64_t	serviceupid;
	int	algorithm;

	DB_RESULT result;
	DB_ROW	row;

	result = DBselect("select l.serviceupid,s.algorithm from services_links l,services s where s.serviceid=l.serviceupid and l.servicedownid=" ZBX_FS_UI64,
		serviceid);
	status=0;
	while((row=DBfetch(result)))
	{
		ZBX_STR2UINT64(serviceupid,row[0]);
		algorithm=atoi(row[1]);
		if(SERVICE_ALGORITHM_NONE == algorithm)
		{
/* Do nothing */
		}
		else if((SERVICE_ALGORITHM_MAX == algorithm) ||
			(SERVICE_ALGORITHM_MIN == algorithm))
		{
			status = DBget_service_status(serviceupid, algorithm, 0);

			DBadd_service_alarm(serviceupid,status,time(NULL));
			DBexecute("update services set status=%d where serviceid=" ZBX_FS_UI64,
				status,
				serviceupid);
		}
		else
		{
			zabbix_log( LOG_LEVEL_ERR, "Unknown calculation algorithm of service status [%d]",
				algorithm);
			zabbix_syslog("Unknown calculation algorithm of service status [%d]",
				algorithm);
		}
	}
	DBfree_result(result);

	result = DBselect("select serviceupid from services_links where servicedownid=" ZBX_FS_UI64,
		serviceid);

	while((row=DBfetch(result)))
	{
		ZBX_STR2UINT64(serviceupid,row[0]);
		DBupdate_services_rec(serviceupid);
	}
	DBfree_result(result);
}


/******************************************************************************
 *                                                                            *
 * Function: DBupdate_services_status_all                                     *
 *                                                                            *
 * Purpose: Cleaning parent nodes from triggers, updating ALL services status.*
 *                                                                            *
 * Parameters:                                                                *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static void DBupdate_services_status_all(void)
{
	DB_RESULT result;
	DB_ROW rows;

	zbx_uint64_t
		serviceid = 0,
		triggerid = 0;

	int	status = 0;

	DBclear_parents_from_trigger(0);

	result = DBselect("SELECT s.serviceid,s.algorithm,s.triggerid "
			" FROM services AS s "
			" WHERE s.serviceid NOT IN (SELECT DISTINCT sl.serviceupid FROM services_links AS sl)");

	while( (rows = DBfetch(result)) )
	{
		ZBX_STR2UINT64(serviceid, rows[0]);
		ZBX_STR2UINT64(triggerid, rows[2]);

		status = DBget_service_status(serviceid, atoi(rows[1]), triggerid);

		DBexecute("UPDATE services SET status=%i WHERE serviceid=" ZBX_FS_UI64,
			status,
			serviceid);

		DBadd_service_alarm(serviceid, status, time(NULL));
	}
	DBfree_result(result);

	result = DBselect("SELECT MAX(sl.servicedownid) as serviceid, sl.serviceupid "
			" FROM services_links AS sl "
			" WHERE sl.servicedownid NOT IN (select distinct sl.serviceupid from services_links as sl) "
			" GROUP BY sl.serviceupid");

	while( (rows = DBfetch(result)) )
	{
		ZBX_STR2UINT64(serviceid, rows[0]);
		DBupdate_services_rec(serviceid);
	}
	DBfree_result(result);
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_services                                                *
 *                                                                            *
 * Purpose: re-calculate and updates status of the service and its childs     *
 *                                                                            *
 * Parameters: serviceid - item to update services for                        *
 *             status - new status of the service                             *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Alexei Vladishev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
void	DBupdate_services(
		zbx_uint64_t triggerid,
		int status
	)
{
	DB_ROW	row;
	zbx_uint64_t	serviceid;

	DB_RESULT result;

	DBexecute("update services set status=%d where triggerid=" ZBX_FS_UI64,
		status,
		triggerid);

	result = DBselect("select serviceid,algorithm from services where triggerid=" ZBX_FS_UI64,
		triggerid);

	while((row=DBfetch(result)))
	{
		ZBX_STR2UINT64(serviceid,row[0]);
		
		DBadd_service_alarm(
			serviceid,
			status,
			time(NULL)
			);

		DBupdate_services_rec(serviceid);
	}

	DBfree_result(result);
	return;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_service                                                 *
 *                                                                            *
 * Purpose: delete service from database                                      *
 *                                                                            *
 * Parameters: serviceid - service identificator from database                *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_service(
		zbx_uint64_t serviceid
	)
{
	DBexecute("DELETE FROM service_alarms WHERE serviceid=" ZBX_FS_UI64, serviceid);
	DBexecute("delete from services_links where servicedownid=" ZBX_FS_UI64 " or serviceupid=" ZBX_FS_UI64, serviceid, serviceid);
	DBexecute("delete from services where serviceid=" ZBX_FS_UI64, serviceid);
	DBexecute("delete from services_times where serviceid=" ZBX_FS_UI64, serviceid);

	DBupdate_services_status_all();
	
	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_services_by_triggerid                                   *
 *                                                                            *
 * Purpose: delete triggers from service                                      *
 *                                                                            *
 * Parameters: triggerid - trigger identificator from database                *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_services_by_triggerid(
		zbx_uint64_t triggerid
	)
{
	DB_RESULT	db_services;

	DB_ROW		service_data;

	zbx_uint64_t
		serviceid;

	int	result = SUCCEED;

	db_services = DBselect("select serviceid from services where triggerid=" ZBX_FS_UI64, triggerid);

	while( (service_data = DBfetch(db_services)) )
	{
		ZBX_STR2UINT64(serviceid, service_data[0]);
		if( SUCCEED != (result = DBdelete_service(serviceid)) )
			break;
	}

	DBfree_result(db_services);

	return  result;
}


/******************************************************************************
 *                                                                            *
 * Function: DBdelete_link  				                      *
 *                                                                            *
 * Purpose: delete sysmap links from sysmap element                           *
 *                                                                            *
 * Parameters: linkid - link idientificator from database        	      *
 *                                                                            *
 * Return value: always return SUCCEED 				              *
 *                                                                            *
 * Author: Aly			                                              *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_link(zbx_uint64_t linkid){		

	DBexecute("DELETE FROM sysmaps_links WHERE linkid=" ZBX_FS_UI64 ,linkid);
	DBexecute("DELETE FROM sysmaps_link_triggers WHERE linkid=" ZBX_FS_UI64 ,linkid);
return	SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_sysmaps_element                                         *
 *                                                                            *
 * Purpose: delete specified map element                                      *
 *                                                                            *
 * Parameters: selementid - map element identificator from database           *
 *                                                                            *
 * Return value: always return SUCCEED 				              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_sysmaps_element(
		zbx_uint64_t	selementid
	)
{
	DB_RESULT	db_links;
	DB_ROW		link;
	zbx_uint64_t 	linkid;

	db_links = DBselect("select linkid from sysmaps_links where "
		" where selementid1=" ZBX_FS_UI64 " or selementid2=" ZBX_FS_UI64,
		selementid, selementid);
	while( (link = DBfetch(db_links)) ){
		ZBX_STR2UINT64(linkid, link[0]);
		DBdelete_link(linkid);
	}

        DBfree_result(db_links);

	DBexecute("delete from sysmaps_elements where selementid=" ZBX_FS_UI64, selementid);

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_sysmaps_elements_with_triggerid                         *
 *                                                                            *
 * Purpose: delete triggers from map                                          *
 *                                                                            *
 * Parameters: triggerid - trigger dientificator from database                *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_sysmaps_elements_with_triggerid(
		zbx_uint64_t triggerid
	)
{
	DB_RESULT	db_selements;

	DB_ROW		selement_data;

	zbx_uint64_t
		selementid;

	int	result = SUCCEED;

	db_selements = DBselect("select distinct selementid from sysmaps_elements"
		" where elementid=" ZBX_FS_UI64 " and elementtype=%i", triggerid, SYSMAP_ELEMENT_TYPE_TRIGGER);

	while( (selement_data = DBfetch(db_selements)) )
	{
		ZBX_STR2UINT64(selementid, selement_data[0]);
		if( SUCCEED != (result = DBdelete_sysmaps_element(selementid)) )
			break;
	}

	DBfree_result(db_selements);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_trigger                                                 *
 *                                                                            *
 * Purpose: delete trigger from database                                      *
 *                                                                            *
 * Parameters: triggerid - trigger dentificator from database                 *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_trigger(
		zbx_uint64_t triggerid
	)
{
	DB_RESULT	db_triggers;
	DB_RESULT	db_elements;

	DB_ROW		trigger_data;
	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	int result = FAIL;

	db_triggers = DBselect("select description from triggers where triggerid=" ZBX_FS_UI64, triggerid);

	if( (trigger_data = DBfetch(db_triggers)) )
	{
		result = SUCCEED;

		/* first delete child items */
		db_elements = DBselect("select triggerid from triggers where templateid=" ZBX_FS_UI64, triggerid);

		while( (element_data = DBfetch(db_elements)) )
		{ /* recursion */
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( SUCCEED != (result = DBdelete_trigger(elementid)) )
				break;
		}
		
		DBfree_result(db_elements);

		if( SUCCEED == result)
		if( SUCCEED == (result = DBdelete_dependencies_by_triggerid(triggerid)) )
		if( SUCCEED == (result = DBdelete_services_by_triggerid(triggerid)) )
		if( SUCCEED == (result = DBdelete_sysmaps_elements_with_triggerid(triggerid)) )
		{
			DBexecute("delete from trigger_depends where triggerid_up=" ZBX_FS_UI64, triggerid);
			DBexecute("delete from functions where triggerid=" ZBX_FS_UI64, triggerid);
			DBexecute("delete from events where objectid=" ZBX_FS_UI64 " and object=%i", triggerid, EVENT_OBJECT_TRIGGER);

			DBexecute("delete from sysmaps_link_triggers where triggerid=" ZBX_FS_UI64, triggerid);

			/* disable actions */
			db_elements = DBselect("select distinct actionid from conditions "
				" where conditiontype=%i and value=" ZBX_FS_UI64, CONDITION_TYPE_TRIGGER, triggerid);
			while( (element_data = DBfetch(db_elements)) )
			{
				ZBX_STR2UINT64(elementid, element_data[0]);
				DBexecute("update actions set status=%i where actionid=" ZBX_FS_UI64, ACTION_STATUS_DISABLED, elementid);
			}

			DBfree_result(db_elements);

			/* delete action conditions */
			DBexecute("delete from conditions where conditiontype=%i and value=" ZBX_FS_UI64, CONDITION_TYPE_TRIGGER, triggerid);
			DBexecute("delete from triggers where triggerid=" ZBX_FS_UI64, triggerid);

			zabbix_log( LOG_LEVEL_DEBUG, "Trigger '%s' deleted", trigger_data[0]);
		}
	}

	DBfree_result(db_triggers);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_triggers_by_itemid                                      *
 *                                                                            *
 * Purpose: delete triggers by itemid                                         *
 *                                                                            *
 * Parameters: itemid - item identificator from database                      *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_triggers_by_itemid(
		zbx_uint64_t itemid
	)
{
	DB_RESULT	db_triggers;

	DB_ROW		trigger_data;

	zbx_uint64_t
		triggerid;

	int	result = SUCCEED;

	db_triggers = DBselect("select triggerid from functions where itemid=" ZBX_FS_UI64, itemid);

	while( (trigger_data = DBfetch(db_triggers)) )
	{
		ZBX_STR2UINT64(triggerid, trigger_data[0]);

		if( SUCCEED != ( result = DBdelete_trigger(triggerid)) )
			break;
	}

	DBfree_result(db_triggers);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_trends_by_itemid                                        *
 *                                                                            *
 * Purpose: delete item trends                                                *
 *                                                                            *
 * Parameters: itemid - item identificator from database                      *
 *             use_housekeeper - 0 - to delete imidietly                      *
 *                               1 - delete with housekeeper                  *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_trends_by_itemid(
		zbx_uint64_t itemid,
		unsigned char use_housekeeper
	)
{
	if( use_housekeeper )
	{
		DBexecute("insert into housekeeper (housekeeperid,tablename,field,value)"
			" values (" ZBX_FS_UI64 ", 'trends','itemid'," ZBX_FS_UI64 ")", 
				DBget_maxid("housekeeper","housekeeperid"), itemid);
	}
	else
	{
		DBexecute("delete from trends where itemid=" ZBX_FS_UI64, itemid);
	}
	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_history_by_itemid                                       *
 *                                                                            *
 * Purpose: delete item history                                               *
 *                                                                            *
 * Parameters: itemid - item identificator from database                      *
 *             use_housekeeper - 0 - to delete imidietly                      *
 *                               1 - delete with housekeeper                  *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_history_by_itemid(
		zbx_uint64_t itemid,
		unsigned char use_housekeeper
	)
{
	int	result;

	if( SUCCEED == (result = DBdelete_trends_by_itemid(itemid, use_housekeeper)) )
	{
		if( use_housekeeper )
		{
			DBexecute("insert into housekeeper (housekeeperid,tablename,field,value)"
					" values (" ZBX_FS_UI64 ",'history_text','itemid'," ZBX_FS_UI64 ")", 
						DBget_maxid("housekeeper","housekeeperid"), itemid);
			DBexecute("insert into housekeeper (housekeeperid,tablename,field,value)"
					" values (" ZBX_FS_UI64 ",'history_log','itemid'," ZBX_FS_UI64 ")", 
						DBget_maxid("housekeeper","housekeeperid"), itemid);
			DBexecute("insert into housekeeper (housekeeperid,tablename,field,value)"
							" values (" ZBX_FS_UI64 ",'history_uint','itemid'," ZBX_FS_UI64 ")", 
								DBget_maxid("housekeeper","housekeeperid"), itemid);
			DBexecute("insert into housekeeper (housekeeperid,tablename,field,value)"
							" values (" ZBX_FS_UI64 ",'history_str','itemid'," ZBX_FS_UI64 ")", 
								DBget_maxid("housekeeper","housekeeperid"), itemid);
			DBexecute("insert into housekeeper (housekeeperid,tablename,field,value)"
								" values (" ZBX_FS_UI64 ",'history','itemid'," ZBX_FS_UI64 ")", 
									DBget_maxid("housekeeper","housekeeperid"), itemid);
		}
		else
		{
			DBexecute("delete from history_text where itemid=" ZBX_FS_UI64, itemid);
			DBexecute("delete from history_log where itemid=" ZBX_FS_UI64, itemid);
			DBexecute("delete from history_uint where itemid=" ZBX_FS_UI64, itemid);
			DBexecute("delete from history_str where itemid=" ZBX_FS_UI64, itemid);
			DBexecute("delete from history where itemid=" ZBX_FS_UI64, itemid);
		}
	}
	return result;
}

/******************************************************************************
 *                                                                            *
 * Type: ZBX_GRAPH_ITEMS                                                      *
 *                                                                            *
 * Purpose: represent graph item data                                         *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 ******************************************************************************/
typedef struct {
	zbx_uint64_t	itemid;
	char		*color;
	int		drawtype;
	int		sortorder;
	int		yaxisside;
	int		calc_fnc;
	int		type;
	int		periods_cnt;
} ZBX_GRAPH_ITEMS;

/******************************************************************************
 *                                                                            *
 * Function: zbx_free_gitems                                                  *
 *                                                                            *
 * Purpose: free allocated memory by DBget_same_graphitems_for_host           *
 *                                                                            *
 * Parameters: gitems - zero terminated array of graph items                  *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 ******************************************************************************/
#define zbx_free_gitems(gitems) __zbx_free_gitems(&gitems)

static void	__zbx_free_gitems(
		ZBX_GRAPH_ITEMS **gitems
	)
{
	int i = 0;

	if ( !*gitems )	return;

	for ( i=0; (*gitems)[i].itemid != 0; i++ )
		zbx_free((*gitems)[i].color);

	zbx_free(*gitems)
}

/******************************************************************************
 *                                                                            *
 * Function: DBcmp_graphitems                                                 *
 *                                                                            *
 * Purpose: Compare two graph items                                           *
 *                                                                            *
 * Parameters:                                                                *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcmp_graphitems(
		ZBX_GRAPH_ITEMS *gitem1,
		ZBX_GRAPH_ITEMS	*gitem2
	)
{
	DB_RESULT	db_items;
	DB_ROW		db_item_data;

	if(gitem1->drawtype	!= gitem2->drawtype)	return 1;
	if(gitem1->sortorder	!= gitem2->sortorder)	return 2;
	if(strcmp(gitem1->color, gitem2->color))	return 3;
	if(gitem1->yaxisside	!= gitem2->yaxisside)	return 4;

	db_items = DBselect("select distinct i2.itemid from items i1, items i2 "
			" where i1.itemid=" ZBX_FS_UI64 " and i2.itemid=" ZBX_FS_UI64
			" and i1.key_=i2.key_ ", gitem1->itemid, gitem2->itemid);

	db_item_data = DBfetch(db_items);

	DBfree_result(db_items);
	if ( !db_item_data )				return 5;
	return 0;
}
/******************************************************************************
 *                                                                            *
 * Function: DBget_same_graphitems_for_host                                   *
 *                                                                            *
 * Purpose: Replace items for specified host                                  *
 *                                                                            *
 * Parameters: gitems - zero terminated array of graph items                  *
 *             dest_hostid - destination host id                              *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static ZBX_GRAPH_ITEMS* DBget_same_graphitems_for_host(
		ZBX_GRAPH_ITEMS	*gitems,
		zbx_uint64_t	dest_hostid
	)
{
	DB_RESULT db_items;
	DB_ROW db_item_data;

	ZBX_GRAPH_ITEMS *new_gitems = NULL;

	int i = 0,
	    new_gi = 0;

	new_gitems = zbx_malloc(new_gitems, sizeof(ZBX_GRAPH_ITEMS));
	new_gitems[0].itemid = 0;

	for ( i=0; gitems[i].itemid != 0; i++ )
	{
		db_items = DBselect("select src.itemid from items src, items dest "
				" where dest.itemid=" ZBX_FS_UI64
				" and src.key_=dest.key_ and src.hostid=" ZBX_FS_UI64,
				gitems[i].itemid, dest_hostid);

		if ( (db_item_data = DBfetch(db_items)) )
		{
			ZBX_STR2UINT64(new_gitems[new_gi].itemid, db_item_data[0]);

			new_gitems[new_gi].color	= strdup(gitems[i].color);
			new_gitems[new_gi].drawtype	= gitems[i].drawtype;
			new_gitems[new_gi].sortorder	= gitems[i].sortorder;
			new_gitems[new_gi].yaxisside	= gitems[i].yaxisside;
			new_gitems[new_gi].calc_fnc	= gitems[i].calc_fnc;
			new_gitems[new_gi].type		= gitems[i].type;
			new_gitems[new_gi].periods_cnt	= gitems[i].periods_cnt;

			new_gitems = zbx_realloc(new_gitems, (new_gi+2)*sizeof(ZBX_GRAPH_ITEMS));
			new_gitems[new_gi+1].itemid = 0;

			new_gi++;
		}

		DBfree_result(db_items);

		if ( !db_item_data )
		{
			zbx_free_gitems(new_gitems);
			break;
		}

	}

	return new_gitems;
}

/******************************************************************************
 *                                                                            *
 * Function: DBadd_item_to_graph                                              *
 *                                                                            *
 * Purpose: add item to the graph                                             *
 *                                                                            *
 * Parameters: graphid - graph identificator from database                    *
 *             itemid - item identificator from database                      *
 *             color - character representation of HEX color 'RRGGBB'         *
 *             drawtype - type of line                                        *
 *             sortorder - sort order                                         *
 *             yaxisside - 0 - use x-axis                                     *
 *                         1 - use y-axis                                     *
 *             calc_fnc - type of calculation function                        *
 *             type - type item (simple, aggregated, ...)                     *
 *             periods_cnt - count of aggregated periods                      *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBadd_item_to_graph(
		zbx_uint64_t	graphid,
		zbx_uint64_t	itemid,
		const char	*color,
		int		drawtype,
		int		sortorder,
		int		yaxisside,
		int		calc_fnc,
		int		type,
		int		periods_cnt
	)
{
	zbx_uint64_t
		gitemid;

	char	*color_esc = NULL;

	int	result = SUCCEED;

	gitemid = DBget_maxid("graphs_items","gitemid");

	color_esc = DBdyn_escape_string(color);

	DBexecute("insert into graphs_items"
		" (gitemid,graphid,itemid,color,drawtype,sortorder,yaxisside,calc_fnc,type,periods_cnt)"
		" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ",'%s',%i,%i,%i,%i,%i,%i)",
				gitemid,
				graphid,
				itemid,
				color_esc,
				drawtype,
				sortorder,
				yaxisside,
				calc_fnc,
				type,
				periods_cnt
			);

	zbx_free(color_esc);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_graph                                                   *
 *                                                                            *
 * Purpose: delete graph from database                                        *
 *                                                                            *
 * Parameters: graphid - graph identificator from database                    *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_graph(
		zbx_uint64_t graphid
	)
{
	DB_RESULT	db_graphs;
	DB_RESULT	db_elements;

	DB_ROW		graph_data;
	DB_ROW		element_data;

	int result = SUCCEED;

	db_graphs = DBselect("select name from graphs where graphid=" ZBX_FS_UI64, graphid);

	if( (graph_data = DBfetch(db_graphs)) )
	{
		/* first delete child graphs */
		db_elements = DBselect("select graphid from graphs where templateid=" ZBX_FS_UI64, graphid);

		while( (element_data = DBfetch(db_elements)) )
		{ /* recursion */
			ZBX_STR2UINT64(graphid, element_data[0]);
			if( SUCCEED != (result = DBdelete_graph(graphid)) )
				return result;
		}

		DBfree_result(db_elements);

		if( SUCCEED == result )
		{ /* delete graph */
			DBexecute("delete from screens_items where resourceid=" ZBX_FS_UI64 " and resourcetype=%i", graphid, SCREEN_RESOURCE_GRAPH);

			DBexecute("delete from graphs_items where graphid=" ZBX_FS_UI64, graphid);
			DBexecute("delete from graphs where graphid=" ZBX_FS_UI64, graphid);

			zabbix_log( LOG_LEVEL_DEBUG, "Graph '%s' deleted", graph_data[0]);
		}
	}

	DBfree_result(db_graphs);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_graph                                                   *
 *                                                                            *
 * Purpose: Update graph without items and recursion for template             *
 *                                                                            *
 * Parameters:                                                                *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBupdate_graph(
		zbx_uint64_t	graphid,
		char 		*name,
		int		width,
		int		height,
		int		yaxistype,
		int		yaxismin,
		int		yaxismax,
		int		show_work_period,
		int		show_triggers,
		int		graphtype,
		int		show_legend,
		int		show_3d,
		zbx_uint64_t	templateid
	)
{
	DB_RESULT db_graphs;
	DB_ROW db_graph_data;

	char	*name_esc = NULL;
	int	old_graphtype = 0;

	db_graphs = DBselect("select graphtype from graphs where graphid=" ZBX_FS_UI64, graphid);
	if ( (db_graph_data = DBfetch(db_graphs)) )
	{
		old_graphtype = atoi(db_graph_data[0]);
	}
	DBfree_result(db_graphs);

	name_esc = DBdyn_escape_string(name);

	DBexecute("update graphs set name='%s',width=%i,height=%i,"
		"yaxistype=%i,yaxismin=%i,yaxismax=%i,templateid=" ZBX_FS_UI64 ","
		"show_work_period=%i,show_triggers=%i,graphtype=%i,"
		"show_legend=%i,show_3d=%i where graphid=" ZBX_FS_UI64,
		name_esc,width,height,yaxistype,yaxismin,yaxismax,templateid,show_work_period,show_triggers,graphtype,
		show_legend,show_3d,graphid);

	zbx_free(name_esc);

	if( old_graphtype != graphtype && graphtype == GRAPH_TYPE_STACKED)
	{
		DBexecute("update graphs_items set calc_fnc=%i,drawtype=1,type=%i "
			" where graphid=" ZBX_FS_UI64,
			CALC_FNC_AVG, GRAPH_ITEM_SIMPLE, graphid);
	}

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_graph_with_items                                        *
 *                                                                            *
 * Purpose: Update graph with items and recursion for template                *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBupdate_graph_with_items(
		zbx_uint64_t	graphid,
		char		*name,
		int		width,
		int		height,
		int		yaxistype,
		int		yaxismin,
		int		yaxismax,
		int		show_work_period,
		int		show_triggers,
		int		graphtype,
		int		show_legend,
		int		show_3d,
		ZBX_GRAPH_ITEMS	*gitems,
		zbx_uint64_t	templateid
	)
{
	DB_RESULT	db_hosts;
	DB_ROW		db_host_data;

	DB_RESULT	db_item_hosts;
	DB_ROW		db_item_data;

	DB_RESULT	chd_graphs;
	DB_ROW		chd_graph_data;

	zbx_uint64_t	curr_hostid = 0;
	int		curr_host_status = 0;

	zbx_uint64_t
		new_hostid = 0,
		chd_graphid = 0,
		chd_hostid = 0;

	char		*itemids = NULL;

	ZBX_GRAPH_ITEMS	*new_gitems = NULL;

	int	i = 0;
	int	result = SUCCEED;

	if ( !gitems )
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Missed items for graph '%s'", name);
		return FAIL;
	}

	/* check items for template graph */
	db_hosts = DBselect("select distinct h.hostid, h.status from hosts h, items i, graphs_items gi "
			" where h.hostid=i.hostid and i.itemid=gi.itemid and gi.graphid=" ZBX_FS_UI64, graphid);
	if ( (db_host_data = DBfetch(db_hosts)) )
	{
		ZBX_STR2UINT64(curr_hostid, db_host_data[0]);

		curr_host_status = atoi(db_host_data[1]);
	}
	DBfree_result(db_hosts);

	if ( curr_host_status == HOST_STATUS_TEMPLATE )
	{
		itemids = zbx_dsprintf(itemids, "0");
		for ( i = 0; gitems[i].itemid != 0; i++ )
		{
			itemids = zbx_strdcatf(itemids, "," ZBX_FS_UI64, gitems[i].itemid);
		}

		db_item_hosts = DBselect("select distinct hostid from items where itemid in (%s)", itemids);

		zbx_free(itemids);

		new_hostid = 0;

		while( (db_item_data = DBfetch(db_item_hosts)) )
		{
			if ( new_hostid )
			{
				zabbix_log(LOG_LEVEL_DEBUG, "Can not use multiple host items for template graph '%s'", name);
				result = FAIL;
				break;
			}

			ZBX_STR2UINT64(new_hostid, db_item_data[0]);
		}

		DBfree_result(db_item_hosts);

		if ( SUCCEED == result && curr_hostid != new_hostid )
		{
			zabbix_log(LOG_LEVEL_DEBUG, "You must use items only from host " ZBX_FS_UI64 " for template graph '%s'", curr_hostid, name);
			result = FAIL;
		}

		if ( SUCCEED != result )
		{
			return result;
		}
	}

	/* firstly update child graphs */
	chd_graphs = DBselect("select g.graphid from graphs g where g.templateid=" ZBX_FS_UI64, graphid);
	while ( (chd_graph_data = DBfetch(chd_graphs)) )
	{
		chd_hostid = 0;
		ZBX_STR2UINT64(chd_graphid, chd_graph_data[0]);

		db_hosts = DBselect("select distinct i.hostid from items i, graphs_items gi "
				" i.itemid=gi.itemid and gi.graphid=" ZBX_FS_UI64, chd_graphid);
		if ( (db_host_data = DBfetch(db_hosts)) )
		{
			ZBX_STR2UINT64(chd_hostid, db_host_data[0]);
		}
		DBfree_result(db_hosts);

		if ( ! (new_gitems = DBget_same_graphitems_for_host(gitems, chd_hostid)) )
		{ /* skip host with missed items */
			zabbix_log(LOG_LEVEL_DEBUG, "Can not update graph '%s' for host " ZBX_FS_UI64, name, chd_hostid);
			result = FAIL;
		}
		else
		{
			result = DBupdate_graph_with_items(chd_graphid, name, width, height,
				yaxistype, yaxismin, yaxismax,
				show_work_period, show_triggers, graphtype, show_legend,
				show_3d, new_gitems, graphid);

			zbx_free_gitems(new_gitems);
		}

		if ( SUCCEED != result )
		{
			return result;
		}
	}
	DBfree_result(chd_graphs);

	DBexecute("delete from graphs_items where graphid=" ZBX_FS_UI64, graphid);

	for ( i=0; gitems[i].itemid != 0; i++ )
	{
		if ( SUCCEED != (result = DBadd_item_to_graph(
				graphid,
				gitems[i].itemid,
				gitems[i].color,
				gitems[i].drawtype,
				gitems[i].sortorder,
				gitems[i].yaxisside,
				gitems[i].calc_fnc,
				gitems[i].type,
				gitems[i].periods_cnt)) )
		{
			return result;
		}
	}

	if ( SUCCEED == (result = DBupdate_graph(graphid,name,width,height,yaxistype,yaxismin,yaxismax,show_work_period,
					show_triggers,graphtype,show_legend,show_3d,templateid)) )
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Graph '%s' updated for hosts " ZBX_FS_UI64, name, curr_hostid);
	}

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBadd_graph                                                      *
 *                                                                            *
 * Purpose: add graph                                                         *
 *                                                                            *
 * Parameters: new_graphid - return created graph database identificator      *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBadd_graph(
		zbx_uint64_t	*new_graphid,
		const char	*name,
		int		width,
		int		height,
		int		yaxistype,
		int		yaxismin,
		int		yaxismax,
		int		show_work_period,
		int		show_triggers,
		int		graphtype,
		int		show_legend,
		int		show_3d,
		zbx_uint64_t	templateid
	)
{
	zbx_uint64_t
		graphid;

	char	*name_esc = NULL;

	graphid = DBget_maxid("graphs","graphid");

	assert(name);

	name_esc = DBdyn_escape_string(name);

	DBexecute("insert into graphs"
		" (graphid,name,width,height,yaxistype,yaxismin,yaxismax,show_work_period,show_triggers,graphtype,show_legend,show_3d,templateid)"
		" values (" ZBX_FS_UI64 ",'%s',%i,%i,%i,%i,%i,%i,%i,%i,%i,%i," ZBX_FS_UI64 ")",
				graphid,
				name_esc,
				width,
				height,
				yaxistype,
				yaxismin,
				yaxismax,
				show_work_period,
				show_triggers,
				graphtype,
				show_legend,
				show_3d,
				templateid);
	zbx_free(name_esc);

	if( new_graphid )
		*new_graphid = graphid;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBadd_graph_with_items                                           *
 *                                                                            *
 * Purpose: Add graph with items and recursion for templates                  *
 *                                                                            *
 * Parameters: new_graphid - return created graph database identificator      *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_graph_to_host(
		zbx_uint64_t	graphid,
		zbx_uint64_t	hostid,
		unsigned char copy_mode
	);
static int	DBadd_graph_with_items(
		zbx_uint64_t	*new_graphid,
		const char	*name,
		int		width,
		int		height,
		int		yaxistype,
		int		yaxismin,
		int		yaxismax,
		int		show_work_period,
		int		show_triggers,
		int		graphtype,
		int		show_legend,
		int		show_3d,
		ZBX_GRAPH_ITEMS *gitems,
		zbx_uint64_t	templateid
	)
{
	DB_RESULT	db_item_hosts;
	DB_ROW		db_item_host;

	DB_RESULT	db_chd_hosts;
	DB_ROW		chd_host_data;
	
	zbx_uint64_t	chd_hostid = 0;

	char *itemids = NULL;

	int 	i = 0,
		new_host_count = 0,
		new_host_is_template = 0;

	int result = FAIL;

	if ( !gitems )
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Missed items for graph '%s'", name);
		return result;
	}

	/* check items for template graph */
	itemids = zbx_dsprintf(itemids, "0");
	for ( i=0; gitems[i].itemid != 0; i++ )
		itemids = zbx_strdcatf(itemids, "," ZBX_FS_UI64, gitems[i].itemid);

	db_item_hosts = DBselect("select distinct h.hostid,h.host,h.status "
			" from items i, hosts h where h.hostid=i.hostid and i.itemid in (%s)", itemids);

	zbx_free(itemids);

	new_host_count = 0;
	new_host_is_template = 0;
	while( (db_item_host = DBfetch(db_item_hosts)) )
	{
		new_host_count++;
		if ( HOST_STATUS_TEMPLATE == atoi(db_item_host[2]) )
			new_host_is_template = 1;
	}
	DBfree_result(db_item_hosts);

	if ( new_host_is_template && new_host_count > 1 )
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Graph '%s' with template host can not contain items from other hosts.", name);
		return result;
	}


	if ( SUCCEED == (result = DBadd_graph(new_graphid, name,width,height,yaxistype,yaxismin,yaxismax,show_work_period,show_triggers,graphtype,show_legend,show_3d,templateid)) )
	{
		for ( i=0; gitems[i].itemid != 0; i++ )
		{
			if ( SUCCEED != (result = DBadd_item_to_graph(
				*new_graphid,
				gitems[i].itemid,
				gitems[i].color,
				gitems[i].drawtype,
				gitems[i].sortorder,
				gitems[i].yaxisside,
				gitems[i].calc_fnc,
				gitems[i].type,
				gitems[i].periods_cnt)) )
			{
				break;
			}
		}
	}

	if ( SUCCEED == result )
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Graph '%s' added", name);

		/* add graphs for child hosts */
		db_chd_hosts = DBselect("select distinct ht.hostid "
				" from hosts_templates ht, items i, graphs_items gi "
				" where ht.templateid=i.hostid and i.itemid=gi.itemid and gi.graphid=" ZBX_FS_UI64, *new_graphid);

		while( (chd_host_data = DBfetch(db_chd_hosts)) )
		{
			ZBX_STR2UINT64(chd_hostid, chd_host_data[0]);

			DBcopy_graph_to_host(*new_graphid, chd_hostid, 0);
		}

		DBfree_result(db_chd_hosts);
	}

	if ( SUCCEED != result && *new_graphid )
	{
		DBdelete_graph(*new_graphid);
		*new_graphid = 0;
	}

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_item                                                    *
 *                                                                            *
 * Purpose: delete item from database                                         *
 *                                                                            *
 * Parameters: itemid - item identificator from database                      *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_item(
		zbx_uint64_t itemid
	)
{
	DB_RESULT	db_items;
	DB_RESULT	db_elements;

	DB_ROW		item_data;
	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	int result = SUCCEED;

	db_items = DBselect("select i.itemid ,i.key_,h.host from items i,"
				" hosts h where h.hostid=i.hostid and i.itemid=" ZBX_FS_UI64, itemid);

	if( (item_data = DBfetch(db_items)) )
	{
		/* first delete child items */
		db_elements = DBselect("select itemid from items where templateid=" ZBX_FS_UI64, itemid);

		while( (element_data = DBfetch(db_elements)) )
		{/* recursion */
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( SUCCEED != (result = DBdelete_item(elementid)) )
				break;
		}
		
		DBfree_result(db_elements);

		if( SUCCEED == result)
		if( SUCCEED == (result = DBdelete_triggers_by_itemid(itemid)) )
		if( SUCCEED == (result = DBdelete_history_by_itemid(itemid, 1 /* use housekeeper */)) )
		{
			DBexecute("delete from screens_items where resourceid=" ZBX_FS_UI64 " and resourcetype in (%i,%i)",
					itemid, SCREEN_RESOURCE_PLAIN_TEXT, SCREEN_RESOURCE_SIMPLE_GRAPH);

			DBexecute("delete from graphs_items where itemid=" ZBX_FS_UI64, itemid);
			DBexecute("delete from items_applications where itemid=" ZBX_FS_UI64, itemid);
			DBexecute("delete from items where itemid=" ZBX_FS_UI64, itemid);

			zabbix_log( LOG_LEVEL_DEBUG, "Item '%s:%s' deleted", item_data[2], item_data[1]);
		}
	}

	DBfree_result(db_items);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_application                                             *
 *                                                                            *
 * Purpose: delete application                                                *
 *                                                                            *
 * Parameters: applicationid - application identificator from database        *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_application(
		zbx_uint64_t applicationid
	)
{
	DB_RESULT	db_applicatoins;
	DB_RESULT	db_elements;

	DB_ROW		application_data;
	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	int result = FAIL;

	db_applicatoins = DBselect("select a.applicationid,a.name,h.host from applications a,"
				" hosts h where h.hostid=a.hostid and a.applicationid=" ZBX_FS_UI64, applicationid);

	if( (application_data = DBfetch(db_applicatoins)) )
	{
		result = SUCCEED;

		/* first delete child applications */
		db_elements = DBselect("select applicationid from applications where templateid=" ZBX_FS_UI64, applicationid);

		while( (element_data = DBfetch(db_elements)) )
		{/* recursion */
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( SUCCEED != (result = DBdelete_application(elementid)) )
				break;
		}
		
		DBfree_result(db_elements);

		if( SUCCEED == result )
		{
			db_elements = DBselect("select name from httptest where applicationid=" ZBX_FS_UI64, applicationid);

			if( (element_data = DBfetch(db_elements)) )
			{
				zabbix_log( LOG_LEVEL_DEBUG, "Application '%s:%s' used by scenario '%s'", application_data[2], application_data[1], element_data[0]);
				result = FAIL;
			}

			DBfree_result(db_elements);
		} 

		if( SUCCEED == result )
		{
			db_elements = DBselect("select i.key_, i.description from items_applications ia, items i "
					" where i.type=%i and i.itemid=ia.itemid and ia.applicationid=" ZBX_FS_UI64, ITEM_TYPE_HTTPTEST, applicationid);
			if( (element_data = DBfetch(db_elements)) )
			{
				zabbix_log( LOG_LEVEL_DEBUG, "Application '%s:%s' used by item '%s'", application_data[2], application_data[1], element_data[0]);
				result = FAIL;
			}

			DBfree_result(db_elements);
		}

		if( SUCCEED == result )
		{
			DBexecute("delete from items_applications where applicationid=" ZBX_FS_UI64, applicationid);
			DBexecute("delete from applications where applicationid=" ZBX_FS_UI64, applicationid);

			zabbix_log( LOG_LEVEL_DEBUG, "Application '%s:%s' deleted", application_data[2], application_data[1]);
		}
	}

	DBfree_result(db_applicatoins);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_template_graphs                                         *
 *                                                                            *
 * Purpose: delete template graphs from host                                  *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             unlink_mode - 1 - only unlink elements without deletion        *
 *                           0 - delete elements                              *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static void	DBdelete_template_graphs(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char unlink_mode
	)
{
	DB_RESULT	db_graphs;
	DB_RESULT	db_tmp_hosts;

	DB_ROW		graph_data;
	DB_ROW		tmp_host_data;

	zbx_uint64_t
		g_templateid,
		tmp_hostid,
		graphid;

	db_graphs = DBselect("select distinct g.graphid,g.templateid,g.name from graphs g, graphs_items gi, items i"
                        " where g.templateid<> 0 and g.graphid=gi.graphid and gi.itemid=i.itemid and i.hostid=" ZBX_FS_UI64, hostid);

	while( (graph_data = DBfetch(db_graphs)) )
	{
		ZBX_STR2UINT64(g_templateid, graph_data[1]);
		
		if( 0 != templateid )
		{
			tmp_hostid = 0;

			db_tmp_hosts = DBselect("select distinct h.hostid from graphs_items gi, items i, hosts h"
						" where h.hostid=i.hostid and gi.itemid=i.itemid and gi.graphid=" ZBX_FS_UI64, g_templateid);

			if( (tmp_host_data = DBfetch(db_tmp_hosts)) )
			{
				ZBX_STR2UINT64(tmp_hostid, tmp_host_data[0]);
			}

			DBfree_result(db_tmp_hosts);

			if(tmp_hostid != templateid)
				continue;
		}

		ZBX_STR2UINT64(graphid, graph_data[0]);

		if( unlink_mode )
		{
			DBexecute("update graphs set templateid=0 where graphid=" ZBX_FS_UI64, graphid);

			zabbix_log( LOG_LEVEL_DEBUG, "Graph '%s' unlinked", graph_data[2]);
		}
		else
		{
			DBdelete_graph(graphid);
		}
	}

	DBfree_result(db_graphs);
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_template_triggers                                       *
 *                                                                            *
 * Purpose: delete template triggers from host                                *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             unlink_mode - 1 - only unlink elements without deletion        *
 *                           0 - delete elements                              *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static void	DBdelete_template_triggers(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char unlink_mode
	)
{
	DB_RESULT	db_triggers;
	DB_RESULT	db_tmp_hosts;

	DB_ROW		trigger_data;
	DB_ROW		tmp_host_data;

	zbx_uint64_t
		t_templateid,
		tmp_hostid,
		triggerid;
	
	db_triggers = DBselect("select distinct t.triggerid,t.templateid,t.description from triggers t, functions f, items i"
                        " where i.hostid=" ZBX_FS_UI64 " and f.itemid=i.itemid and f.triggerid=t.triggerid", hostid);

	while( (trigger_data = DBfetch(db_triggers)) )
	{
		ZBX_STR2UINT64(t_templateid, trigger_data[1]);

		
		if( 0 == t_templateid )
			continue;

		if( 0 != templateid )
		{
			tmp_hostid = 0;

			db_tmp_hosts = DBselect("select distinct h.hostid from hosts h, functions f, items i"
						" where i.itemid=f.itemid and h.hostid=i.hostid and f.triggerid=" ZBX_FS_UI64, t_templateid);

			if( (tmp_host_data = DBfetch(db_tmp_hosts)) )
			{
				ZBX_STR2UINT64(tmp_hostid, tmp_host_data[0]);
			}

			DBfree_result(db_tmp_hosts);

			if(tmp_hostid != templateid)
				continue;
		}

		ZBX_STR2UINT64(triggerid, trigger_data[0]);

		if( unlink_mode )
		{
			DBexecute("update triggers set templateid=0 where triggerid=" ZBX_FS_UI64, triggerid);

			zabbix_log( LOG_LEVEL_DEBUG, "Trigger '%s' unlinked", trigger_data[2]);
		}
		else
		{
			DBdelete_trigger(triggerid);
		}
	}

	DBfree_result(db_triggers);
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_template_items                                          *
 *                                                                            *
 * Purpose: delete template items from host                                   *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             unlink_mode - 1 - only unlink elements without deletion        *
 *                           0 - delete elements                              *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static void	DBdelete_template_items(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char unlink_mode
	)
{
	DB_RESULT	db_items;
	DB_RESULT	db_tmp_hosts;

	DB_ROW		item_data;
	DB_ROW		tmp_host_data;

	zbx_uint64_t
		i_templateid,
		tmp_hostid,
		itemid;
	
	db_items = DBselect("select itemid,templateid,key_ from items where hostid=" ZBX_FS_UI64, hostid);

	while( (item_data = DBfetch(db_items)) )
	{
		ZBX_STR2UINT64(i_templateid, item_data[1]);
		
		if( 0 == i_templateid )
			continue;

		if( 0 != templateid )
		{
			db_tmp_hosts = DBselect("select itemid,templateid,key_ from items where hostid=" ZBX_FS_UI64, i_templateid);

			tmp_hostid = 0;

			if( (tmp_host_data = DBfetch(db_tmp_hosts)) )
			{
				ZBX_STR2UINT64(tmp_hostid, tmp_host_data[0]);
			}

			DBfree_result(db_tmp_hosts);

			if(tmp_hostid != templateid)
				continue;
		}

		ZBX_STR2UINT64(itemid, item_data[0]);

		if( unlink_mode )
		{
			DBexecute("update items set templateid=0 where itemid=" ZBX_FS_UI64, itemid);

			zabbix_log( LOG_LEVEL_DEBUG, "Item '%s' unlinked", item_data[2]);
		}
		else
		{
			DBdelete_item(itemid);
		}
	}

	DBfree_result(db_items);
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_template_applications                                   *
 *                                                                            *
 * Purpose: delete application                                                *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             unlink_mode - 1 - only unlink elements without deletion        *
 *                           0 - delete elements                              *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static void	DBdelete_template_applications(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char unlink_mode
	)
{
	DB_RESULT	db_applications;
	DB_RESULT	db_tmp_hosts;

	DB_ROW		application_data;
	DB_ROW		tmp_host_data;

	zbx_uint64_t
		a_templateid,
		tmp_hostid,
		applicationid;
	
	db_applications = DBselect("select applicationid,templateid,name from applications where hostid=" ZBX_FS_UI64, hostid);

	while( (application_data = DBfetch(db_applications)) )
	{
		ZBX_STR2UINT64(a_templateid, application_data[1]);

		
		if( 0 == a_templateid )
			continue;

		if( 0 != templateid )
		{
			tmp_hostid = 0;

			db_tmp_hosts = DBselect("select hostid from applications where applicationid=" ZBX_FS_UI64, a_templateid);

			if( (tmp_host_data = DBfetch(db_tmp_hosts) ))
			{
				ZBX_STR2UINT64(tmp_hostid, tmp_host_data[0]);
			}

			DBfree_result(db_tmp_hosts);

			if(tmp_hostid != templateid)
				continue;
		}

		ZBX_STR2UINT64(applicationid, application_data[0]);

		if( unlink_mode )
		{
			DBexecute("update applications set templateid=0 where applicationid=" ZBX_FS_UI64, applicationid);

			zabbix_log( LOG_LEVEL_DEBUG, "Application '%s' unlinked", application_data[2]);
		}
		else
		{
			DBdelete_application(applicationid);
		}
	}

	DBfree_result(db_applications);
}

/******************************************************************************
 *                                                                            *
 * Function: DBdb_save_application                                            *
 *                                                                            *
 * Purpose: add or update application                                         *
 *                                                                            *
 * Parameters: name                                                           *
 *             hostid - host identificator from database                      *
 *             applicationid - application identificator from database        *
 *                             0 - for adding                                 *
 *             templateid - template application identificator from database  *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdb_save_application(
		const char	*name,
		zbx_uint64_t	hostid,
		zbx_uint64_t	applicationid,
		zbx_uint64_t	templateid
	)
{
	DB_RESULT	db_elements;
	DB_RESULT	db_hosts;

	DB_ROW		element_data;
	DB_ROW		host_data;

	zbx_uint64_t	applicationid_new = 0,
			elementid,
			db_hostid;

	char	*name_esc = NULL;

	int	result = SUCCEED;

	assert(name);

	name_esc = DBdyn_escape_string(name);

	if( 0 == applicationid )
		db_elements = DBselect("select distinct applicationid from applications "
				" where name='%s' and hostid=" ZBX_FS_UI64, name_esc, hostid);
	else
		db_elements = DBselect("select distinct applicationid from applications "
				" where name='%s' and hostid=" ZBX_FS_UI64
				" and applicationid<>" ZBX_FS_UI64, name_esc, hostid, applicationid);

	if( (element_data = DBfetch(db_elements)) )
	{
		ZBX_STR2UINT64(elementid, element_data[0]);

		if( 0 == templateid )
		{
			zabbix_log(LOG_LEVEL_DEBUG, "Application '%s' already exists", name);
			result = FAIL;
		}
		else if ( 0 != applicationid )
		{ /* delete old application with same name */
			DBdelete_application(elementid);
		}
		else
		{ /* if found application with same name update them, adding not needed */
			applicationid = elementid;
		}
	}

	DBfree_result(db_elements);

	if( SUCCEED == result )
	{
		db_hosts = DBselect("select host from hosts where hostid=" ZBX_FS_UI64, hostid);

		if( (host_data = DBfetch(db_hosts)) )
		{
			if( 0 == applicationid )
			{
				applicationid_new = DBget_maxid("applications","applicationid");

				DBexecute("insert into applications (applicationid,name,hostid,templateid)"
					" values (" ZBX_FS_UI64 ",'%s'," ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
					applicationid_new, name_esc, hostid, templateid);

				zabbix_log( LOG_LEVEL_DEBUG, "Added new application '%s:%s'", host_data[0], name);
			}
			else
			{
				DBexecute("update applications set name='%s',hostid=" ZBX_FS_UI64 ",templateid=" ZBX_FS_UI64
					" where applicationid=" ZBX_FS_UI64, name_esc, hostid, templateid, applicationid);

				zabbix_log( LOG_LEVEL_DEBUG, "Updated application [" ZBX_FS_UI64 "] '%s:%s'", applicationid, host_data[0], name);
			}
		}

		DBfree_result(db_hosts);
	}

	if( SUCCEED == result)
	{
		if( 0 == applicationid )
		{ /* create application for childs */
			applicationid = applicationid_new;

			db_hosts = DBselect("select distinct hostid from hosts_templates where templateid=" ZBX_FS_UI64, hostid);

			while( (host_data = DBfetch(db_hosts)) )
			{ /* recursion */
				ZBX_STR2UINT64(elementid, host_data[0]);
				if( SUCCEED != (result = DBadd_application(name, elementid, applicationid)) )
					break;
			}

			DBfree_result(db_hosts);
		}
		else
		{
			db_elements = DBselect("select applicationid,hostid from applications where templateid=" ZBX_FS_UI64, applicationid);

			while( (element_data = DBfetch(db_elements)) )
			{ /* recursion */
				ZBX_STR2UINT64(elementid, element_data[0]);
				ZBX_STR2UINT64(db_hostid, element_data[1]);
				if( SUCCEED != (result = DBupdate_application(elementid, name, db_hostid, applicationid)) )
					break;
			}
			
			DBfree_result(db_elements);
		}

		if( SUCCEED != result && 0 == templateid )
		{
			DBdelete_application(applicationid);
		}
	}

	zbx_free(name_esc);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_template_applications                                     *
 *                                                                            *
 * Purpose: copy applications from template to host                           *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_template_applications(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char copy_mode
	)
{
	DB_RESULT	db_elements;

	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	int	result = SUCCEED;

	if( 0 == templateid)
	{ /* sync with all linkage templates */
		db_elements = DBselect("select templateid from hosts_templates where hostid=" ZBX_FS_UI64, hostid);

		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( 0 == elementid ) continue;

			/* recursion */
			if( SUCCEED != (result = DBcopy_template_applications(hostid, elementid, copy_mode)) )
				break;
		}

		DBfree_result(db_elements);
	}
	else
	{
		db_elements = DBselect("select applicationid,name from applications where hostid=" ZBX_FS_UI64, templateid);
		
		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( SUCCEED != (result = DBadd_application(element_data[1], hostid, copy_mode ? 0 : elementid)) )
				break;
		}

		DBfree_result(db_elements);
	}

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_item                                                    *
 *                                                                            *
 * Purpose: udate item                                                        *
 *                                                                            *
 * Parameters: itemid - item identificator from database                      *
 *             description                                                    *
 *             key                                                            *
 *             hostid - host identificator from database                      *
 *             delay                                                          *
 *             history                                                        *
 *             status                                                         *
 *             type                                                           *
 *             snmp_community                                                 *
 *             snmp_oid                                                       *
 *             value_type                                                     *
 *             trapper_hosts                                                  *
 *             snmp_port                                                      *
 *             units                                                          *
 *             multiplier                                                     *
 *             delta                                                          *
 *             snmpv3_securityname                                            *
 *             snmpv3_securitylevel                                           *
 *             snmpv3_authpassphrase                                          *
 *             snmpv3_privpassphrase                                          *
 *             formula                                                        *
 *             trends                                                         *
 *             logtimefmt                                                     *
 *             valuemapid                                                     *
 *             delay_flex                                                     *
 *             params                                                         *
 *             apps - zero teminated array of applicationid                   *
 *             templateid - template item identificator from database         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBupdate_item(
		zbx_uint64_t	itemid,
		const char	*description,
		const char	*key,
		zbx_uint64_t	hostid,
		int 		delay,
		int 		history,
		int		status,
		int		type,
		const char	*snmp_community,
		const char	*snmp_oid,
		int		value_type,
		const char      *trapper_hosts,
		int		snmp_port,
		const char      *units,
		int		multiplier,
		int		delta,
		const char      *snmpv3_securityname,
		int		snmpv3_securitylevel,
		const char      *snmpv3_authpassphrase,
		const char      *snmpv3_privpassphrase,
		const char      *formula,
		int		trends,
		const char      *logtimefmt,
		zbx_uint64_t	valuemapid,
		const char      *delay_flex,
		const char	*params,
		zbx_uint64_t	*apps,
		zbx_uint64_t	templateid
	)
{
	register int	i = 0;

	DB_RESULT	db_hosts;
	DB_RESULT	db_items;
	DB_RESULT	db_chd_items;

	DB_ROW		host_data;
	DB_ROW		item_data;
	DB_ROW		chd_item_data;

	zbx_uint64_t
		itemappid,
		del_itemid = 0,
		chd_itemidm,
		chd_hostid,
		applications[ZBX_MAX_APPLICATIONS];

	char	*description_esc,
		*key_esc,
		*snmp_community_esc,
		*snmp_oid_esc,
		*trapper_hosts_esc,
		*units_esc,
		*snmpv3_securityname_esc,
		*snmpv3_authpassphrase_esc,
		*snmpv3_privpassphrase_esc,
		*formula_esc,
		*logtimefmt_esc,
		*delay_flex_esc,
		*params_esc;

	int	result = SUCCEED;

	db_hosts = DBselect("select host from hosts where hostid=" ZBX_FS_UI64, hostid);

	if( (host_data = DBfetch(db_hosts)) )
	{
		key_esc	= DBdyn_escape_string(key);

		if( ITEM_VALUE_TYPE_STR == value_type )
		{
			delta = 0;
		}

		if( (ITEM_TYPE_AGGREGATE == type) )
		{
			value_type = ITEM_VALUE_TYPE_FLOAT;
		}

		db_items = DBselect("select distinct itemid from items"
				" where hostid=" ZBX_FS_UI64 " and itemid<> " ZBX_FS_UI64 " and key_='%s'",
				hostid, itemid, key_esc);

		if( (item_data = DBfetch(db_items)) )
		{
			if( templateid == 0)
			{
				zabbix_log(LOG_LEVEL_DEBUG, "An item with the Key [%s] already exists for host [%s]."
					" The key must be unique.", key, host_data[0]);

				result = FAIL;
			}
			else
			{ /* delete dublicated items */
				ZBX_STR2UINT64(del_itemid, item_data[0]);
			}
		}

		DBfree_result(db_items);

		if( SUCCEED == result )
		{

			/* first update child items */
			db_chd_items = DBselect("select itemid, hostid from items where templateid=" ZBX_FS_UI64, itemid);

			while( (chd_item_data = DBfetch(db_chd_items)) )
			{
				ZBX_STR2UINT64(chd_itemidm, chd_item_data[0]);
				ZBX_STR2UINT64(chd_hostid, chd_item_data[1]);

				DBget_same_applications_for_host(apps, chd_hostid, applications, sizeof(applications) / sizeof(zbx_uint64_t));

				/* recursion */
				if( SUCCEED != (result = DBupdate_item(
					chd_itemidm, description, key, chd_hostid, 
					delay, history, status, type, snmp_community, snmp_oid,
					value_type, trapper_hosts, snmp_port, units, multiplier,
					delta, snmpv3_securityname, snmpv3_securitylevel,
					snmpv3_authpassphrase, snmpv3_privpassphrase, formula,
					trends, logtimefmt, valuemapid,delay_flex,params,
					applications,
					itemid)) )
					break;
			}

			DBfree_result(db_chd_items);

			if( 0 < del_itemid )
			{
				DBdelete_item(del_itemid);
			}

			if( SUCCEED == result )
			{
				DBexecute("update items set lastlogsize=0 where itemid=" ZBX_FS_UI64 " and key_<>'%s'", itemid, key_esc);

				DBexecute("delete from items_applications where itemid=" ZBX_FS_UI64, itemid);

				for( i=0; 0 < apps[i]; i++ )
				{
					itemappid = DBget_maxid("items_applications","itemappid");
					DBexecute("insert into items_applications (itemappid,itemid,applicationid) "
						" values(" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
						itemappid, itemid, apps[i]);
				}

				description_esc			= DBdyn_escape_string(description);
				snmp_community_esc		= DBdyn_escape_string(snmp_community);
				snmp_oid_esc			= DBdyn_escape_string(snmp_oid);
				trapper_hosts_esc		= DBdyn_escape_string(trapper_hosts);
				units_esc			= DBdyn_escape_string(units);
				snmpv3_securityname_esc		= DBdyn_escape_string(snmpv3_securityname);
				snmpv3_authpassphrase_esc	= DBdyn_escape_string(snmpv3_authpassphrase);
				snmpv3_privpassphrase_esc	= DBdyn_escape_string(snmpv3_privpassphrase);
				formula_esc			= DBdyn_escape_string(formula);
				logtimefmt_esc			= DBdyn_escape_string(logtimefmt);
				delay_flex_esc			= DBdyn_escape_string(delay_flex);
				params_esc			= DBdyn_escape_string(params);

				DBexecute(
					"update items set description='%s',key_='%s',"
					"hostid=" ZBX_FS_UI64 ",delay=%i,history=%i,nextcheck=0,status=%i,type=%i,"
					"snmp_community='%s',snmp_oid='%s',"
					"value_type=%i,trapper_hosts='%s',"
					"snmp_port=%i,units='%s',multiplier=%i,delta=%i,"
					"snmpv3_securityname='%s',"
					"snmpv3_securitylevel=%i,"
					"snmpv3_authpassphrase='%s',"
					"snmpv3_privpassphrase='%s',"
					"formula='%s',trends=%i,logtimefmt='%s',"
					"valuemapid=" ZBX_FS_UI64 ",delay_flex='%s',params='%s',"
					"templateid=" ZBX_FS_UI64 " where itemid=" ZBX_FS_UI64,
						description_esc,
						key_esc,
						hostid,
						delay,
						history,
						status,
						type,
						snmp_community_esc,
						snmp_oid_esc,
						value_type,
						trapper_hosts_esc,
						snmp_port,
						units_esc,
						multiplier,
						delta,
						snmpv3_securityname_esc,
						snmpv3_securitylevel,
						snmpv3_authpassphrase_esc,
						snmpv3_privpassphrase_esc,
						formula_esc,
						trends,
						logtimefmt_esc,
						valuemapid,
						delay_flex_esc,
						params_esc,
						templateid,
						itemid);

				zbx_free(description_esc);
				zbx_free(snmp_community_esc);
				zbx_free(snmp_oid_esc);
				zbx_free(trapper_hosts_esc);
				zbx_free(units_esc);
				zbx_free(snmpv3_securityname_esc);
				zbx_free(snmpv3_authpassphrase_esc);
				zbx_free(snmpv3_privpassphrase_esc);
				zbx_free(formula_esc);
				zbx_free(logtimefmt_esc);
				zbx_free(delay_flex_esc);
				zbx_free(params_esc);

				zabbix_log(LOG_LEVEL_DEBUG, "Item '%s:%s' updated", host_data[0], key);
			}
		}
		zbx_free(key_esc);
	}

	DBfree_result(db_hosts);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBadd_item                                                       *
 *                                                                            *
 * Purpose: add item to database                                              *
 *                                                                            *
 * Parameters: description                                                    *
 *             key                                                            *
 *             hostid - host identificator from database                      *
 *             delay                                                          *
 *             history                                                        *
 *             status                                                         *
 *             type                                                           *
 *             snmp_community                                                 *
 *             snmp_oid                                                       *
 *             value_type                                                     *
 *             trapper_hosts                                                  *
 *             snmp_port                                                      *
 *             units                                                          *
 *             multiplier                                                     *
 *             delta                                                          *
 *             snmpv3_securityname                                            *
 *             snmpv3_securitylevel                                           *
 *             snmpv3_authpassphrase                                          *
 *             snmpv3_privpassphrase                                          *
 *             formula                                                        *
 *             trends                                                         *
 *             logtimefmt                                                     *
 *             valuemapid                                                     *
 *             delay_flex                                                     *
 *             params                                                         *
 *             apps - zero teminated array of applicationid                   *
 *             templateid - template item identificator from database         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBadd_item(
		const char	*description,
		const char	*key,
		zbx_uint64_t	hostid,
		int 		delay,
		int 		history,
		int		status,
		int		type,
		const char	*snmp_community,
		const char	*snmp_oid,
		int		value_type,
		const char      *trapper_hosts,
		int		snmp_port,
		const char      *units,
		int		multiplier,
		int		delta,
		const char      *snmpv3_securityname,
		int		snmpv3_securitylevel,
		const char      *snmpv3_authpassphrase,
		const char      *snmpv3_privpassphrase,
		const char      *formula,
		int		trends,
		const char      *logtimefmt,
		zbx_uint64_t	valuemapid,
		const char      *delay_flex,
		const char      *params,
		zbx_uint64_t	*apps,
		zbx_uint64_t	templateid
	)
{
	register int	i = 0;

	DB_RESULT	db_hosts;
	DB_RESULT	db_items;
	DB_RESULT	db_chd_hosts;

	DB_ROW		host_data;
	DB_ROW		item_data;
	DB_ROW		chd_host_data;

	zbx_uint64_t
		itemid,
		chd_hostid,
		itemappid,
		applications[ZBX_MAX_APPLICATIONS];

	char	*description_esc,
		*key_esc,
		*snmp_community_esc,
		*snmp_oid_esc,
		*trapper_hosts_esc,
		*units_esc,
		*snmpv3_securityname_esc,
		*snmpv3_authpassphrase_esc,
		*snmpv3_privpassphrase_esc,
		*formula_esc,
		*logtimefmt_esc,
		*delay_flex_esc,
		*params_esc;

	int	result = SUCCEED;

	db_hosts = DBselect("select host from hosts where hostid=" ZBX_FS_UI64, hostid);

	if( (host_data = DBfetch(db_hosts)) )
	{
		key_esc	= DBdyn_escape_string(key);

		if( ITEM_VALUE_TYPE_STR == value_type )
		{
			delta = 0;
		}

		if( (ITEM_TYPE_AGGREGATE == type) )
		{
			value_type = ITEM_VALUE_TYPE_FLOAT;
		}

		db_items = DBselect("select distinct itemid from items"
				" where hostid=" ZBX_FS_UI64 " and key_='%s'", hostid, key_esc);

		itemid = 0;

		if( (item_data = DBfetch(db_items)) )
		{
			if( templateid == 0)
			{
				zabbix_log(LOG_LEVEL_DEBUG, "An item with the Key [%s] already exists for host [%s]."
					" The key must be unique.", key, host_data[0]);

				result = FAIL;
			} else {
				ZBX_STR2UINT64(itemid, item_data[0]);

				result = DBupdate_item(
					itemid, description, key, hostid,
					delay, history, status, type, snmp_community, snmp_oid,
					value_type, trapper_hosts, snmp_port, units, multiplier,
					delta, snmpv3_securityname, snmpv3_securitylevel,
					snmpv3_authpassphrase, snmpv3_privpassphrase, formula,
					trends, logtimefmt, valuemapid, delay_flex, params,
					apps,
					templateid);
			}
		}

		DBfree_result(db_items);

		if( (itemid && SUCCEED != result) || !item_data )
		{
			// first add mother item
			itemid = DBget_maxid("items","itemid");

			description_esc			= DBdyn_escape_string(description);
			snmp_community_esc		= DBdyn_escape_string(snmp_community);
			snmp_oid_esc			= DBdyn_escape_string(snmp_oid);
			trapper_hosts_esc		= DBdyn_escape_string(trapper_hosts);
			units_esc			= DBdyn_escape_string(units);
			snmpv3_securityname_esc		= DBdyn_escape_string(snmpv3_securityname);
			snmpv3_authpassphrase_esc	= DBdyn_escape_string(snmpv3_authpassphrase);
			snmpv3_privpassphrase_esc	= DBdyn_escape_string(snmpv3_privpassphrase);
			formula_esc			= DBdyn_escape_string(formula);
			logtimefmt_esc			= DBdyn_escape_string(logtimefmt);
			delay_flex_esc			= DBdyn_escape_string(delay_flex);
			params_esc			= DBdyn_escape_string(params);

			DBexecute("insert into items (itemid,description,key_,hostid,delay,history,status,type,"
				"snmp_community,snmp_oid,value_type,trapper_hosts,snmp_port,units,multiplier,delta,"
				"snmpv3_securityname,snmpv3_securitylevel,snmpv3_authpassphrase,snmpv3_privpassphrase,"
				"formula,trends,logtimefmt,valuemapid,delay_flex,params,templateid) values"
				" (" ZBX_FS_UI64 ",'%s','%s'," ZBX_FS_UI64 ",%i,%i,%i,%i,'%s','%s',%i,'%s',%i,'%s',%i,"
				"%i,'%s',%i,'%s','%s','%s',%i,'%s'," ZBX_FS_UI64 ",'%s','%s'," ZBX_FS_UI64 ")",
					itemid,
					description_esc,
					key_esc,
					hostid,
					delay,
					history,
					status,
					type,
					snmp_community_esc,
					snmp_oid_esc,
					value_type,
					trapper_hosts_esc,
					snmp_port,
					units_esc,
					multiplier,
					delta,
					snmpv3_securityname_esc,
					snmpv3_securitylevel,
					snmpv3_authpassphrase_esc,
					snmpv3_privpassphrase_esc,
					formula_esc,
					trends,
					logtimefmt_esc,
					valuemapid,
					delay_flex_esc,
					params_esc,
					templateid);

			zbx_free(description_esc);
			zbx_free(snmp_community_esc);
			zbx_free(snmp_oid_esc);
			zbx_free(trapper_hosts_esc);
			zbx_free(units_esc);
			zbx_free(snmpv3_securityname_esc);
			zbx_free(snmpv3_authpassphrase_esc);
			zbx_free(snmpv3_privpassphrase_esc);
			zbx_free(formula_esc);
			zbx_free(logtimefmt_esc);
			zbx_free(delay_flex_esc);
			zbx_free(params_esc);

			for( i=0; 0 < apps[i]; i++)
			{
				itemappid = DBget_maxid("items_applications","itemappid");

				DBexecute("insert into items_applications (itemappid,itemid,applicationid) "
					" values(" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
					itemappid, itemid, apps[i]);
			}

			/* add items to child hosts */
			db_chd_hosts = DBselect("select hostid from hosts_templates where templateid=" ZBX_FS_UI64, hostid);

			while( (chd_host_data = DBfetch(db_chd_hosts)) )
			{	/* recursion */

				ZBX_STR2UINT64(chd_hostid, chd_host_data[0]);

				DBget_same_applications_for_host(apps, chd_hostid, applications, sizeof(applications) / sizeof(zbx_uint64_t));

				if( SUCCEED != (result = DBadd_item(description, key, chd_hostid,
					delay, history, status, type, snmp_community, snmp_oid,
					value_type, trapper_hosts, snmp_port, units, multiplier,
					delta, snmpv3_securityname, snmpv3_securitylevel,
					snmpv3_authpassphrase, snmpv3_privpassphrase, formula,
					trends, logtimefmt, valuemapid,delay_flex,params,
					applications,
					itemid)) )
						break;
			}

			DBfree_result(db_chd_hosts);

			if( SUCCEED != result )
			{
				DBdelete_item(itemid);
			}
			else
			{
				zabbix_log(LOG_LEVEL_DEBUG, "Added new item '%s:%s'", host_data[0], key);
			}
		}
		zbx_free(key_esc);
	}

	DBfree_result(db_hosts);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_template_items                                            *
 *                                                                            *
 * Purpose: copy template items to host                                       *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_template_items(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char copy_mode
	)
{
	DB_RESULT	db_elements;

	DB_ROW		element_data;

	zbx_uint64_t
		elementid,
		valuemapid;

	int	result = SUCCEED;

	zbx_uint64_t
		tmp_apps[ZBX_MAX_APPLICATIONS],
		apps[ZBX_MAX_APPLICATIONS];

	if( 0 == templateid)
	{ /* sync with all linkage templates */
		db_elements = DBselect("select templateid from hosts_templates where hostid=" ZBX_FS_UI64, hostid);

		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( 0 == elementid ) continue;

			/* recursion */
			if( SUCCEED != (result = DBcopy_template_items(hostid, elementid, copy_mode)) )
				break;
		}

		DBfree_result(db_elements);
	}
	else
	{
		db_elements = DBselect("select itemid,description,key_,delay,history,status,type,snmp_community,"
					"snmp_oid,value_type,trapper_hosts,snmp_port,units,multiplier,delta,"
					"snmpv3_securityname,snmpv3_securitylevel,snmpv3_authpassphrase,"
					"snmpv3_privpassphrase,formula,trends,logtimefmt,valuemapid,delay_flex,params "
					" from items where hostid=" ZBX_FS_UI64, templateid);
		
		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);
			ZBX_STR2UINT64(valuemapid, element_data[22]);

			DBget_applications_by_itemid(elementid, tmp_apps, sizeof(tmp_apps) / sizeof(zbx_uint64_t));
			DBget_same_applications_for_host(tmp_apps, hostid, apps, sizeof(apps) / sizeof(zbx_uint64_t));

			if( SUCCEED != (result = DBadd_item(
							element_data[1],	/* description */
							element_data[2],	/* key_ */
							hostid,
							atoi(element_data[3]),	/* delay */
							atoi(element_data[4]),	/* history */
							atoi(element_data[5]),	/* status */
							atoi(element_data[6]),	/* type */
							element_data[7],	/* snmp_community */
							element_data[8],	/* snmp_oid */
							atoi(element_data[9]),	/* value_type */
							element_data[10],	/* trapper_hosts */
							atoi(element_data[11]),	/* snmp_port */
							element_data[12],	/* units */
							atoi(element_data[13]),	/* multiplier */
							atoi(element_data[14]),	/* delta */
							element_data[15],	/* snmpv3_securityname */
							atoi(element_data[16]),	/* snmpv3_securitylevel */
							element_data[17],	/* snmpv3_authpassphrase */
							element_data[18],	/* snmpv3_privpassphrase */
							element_data[19],	/* formula */
							atoi(element_data[20]),	/* trends */
							element_data[21],	/* logtimefmt */
							valuemapid,		/* valuemapid */
							element_data[23],	/* delay_flex */
							element_data[24],	/* params */
							apps,
							copy_mode ? 0 : elementid))
				)
				break;
		}

		DBfree_result(db_elements);
	}

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBreset_items_nextcheck                                          *
 *                                                                            *
 * Purpose: reset next check timestamps for items                             *
 *                                                                            *
 * Parameters: triggerid = trigger identificator from database                *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBreset_items_nextcheck(
		zbx_uint64_t	triggerid
	)
{
	DB_RESULT	db_functions;

	DB_ROW		function_data;

	zbx_uint64_t
		itemid;

	db_functions = DBselect("select itemid from functions where triggerid=" ZBX_FS_UI64, triggerid);

	while( (function_data = DBfetch(db_functions)) )
	{
		ZBX_STR2UINT64(itemid, function_data[0]);

		DBexecute("update items set nextcheck=0 where itemid=" ZBX_FS_UI64, itemid);
	}

	DBfree_result(db_functions);

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBreplace_template_dependences                                   *
 *                                                                            *
 * Purpose: replace trigger dependences by specified host                     *
 *                                                                            *
 * Parameters: dependences_in - zero terminated array of dependences          *
 *             hostid - host identificator from database                      *
 *             dependences - buffer for result values                         *
 *             max_dependences - size of result buffer in counts              *
 *                                                                            *
 * Return value: count of results                                             *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBreplace_template_dependences(
		zbx_uint64_t	*dependences_in,
		zbx_uint64_t	hostid,
		zbx_uint64_t	*dependences,
		int		max_dependences
	)
{
	DB_RESULT	db_triggers;

	DB_ROW		trigger_data;

	register int	i = 0, j = 0;

	while( 0 < dependences_in[i] )
	{
		db_triggers = DBselect("select t.triggerid from triggers t,functions f,items i "
				" where t.templateid=" ZBX_FS_UI64 " and f.triggerid=t.triggerid "
				" and f.itemid=i.itemid and i.hostid=" ZBX_FS_UI64,
				dependences_in[i], hostid);

		if( j < (max_dependences - 1) && (trigger_data = DBfetch(db_triggers)) )
		{
			ZBX_STR2UINT64(dependences[j], trigger_data[0]);
			j++;
		}

		DBfree_result(db_triggers);

		i++;
	}

	dependences[j] = 0;

	return j;
}

/******************************************************************************
 *                                                                            *
 * Function: DBadd_event                                                      *
 *                                                                            *
 * Purpose: add event without actions processing                              *
 *                                                                            *
 * Parameters: triggerid - trigger identificator from database                *
 *             value - event value                                            *
 *             now - timestamp                                                *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBadd_event(
		zbx_uint64_t	triggerid,
		int		value,
		time_t		now
		)
{
	DB_RESULT	db_events;

	DB_ROW		event_data;

	zbx_uint64_t
		eventid;

	int	result = FAIL;

	if( !now )	now = time(NULL);

	db_events = DBselect("select lastvalue,type from triggers where triggerid=" ZBX_FS_UI64, triggerid);

	if( (event_data = DBfetch(db_events)) )
	{
		if( value != atoi(event_data[0]) )
			result = SUCCEED;
	}

	if( SUCCEED == result )
	{
		eventid = DBget_maxid("events","eventid");

		DBexecute("insert into events(eventid,source,object,objectid,clock,value) "
				" values(" ZBX_FS_UI64 ",%i,%i," ZBX_FS_UI64 ",%lu,%i)",
				eventid, EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, triggerid, now, value);

		if(value == TRIGGER_VALUE_FALSE || value == TRIGGER_VALUE_TRUE)
		{
			DBexecute("update alerts set status=%d,error='Trigger changed its status. Will not send repeats.'"
				" where eventid=" ZBX_FS_UI64 " and status=%d",
				ALERT_STATUS_FAILED,
				eventid,
				ALERT_STATUS_NOT_SENT);
		}
	}

	DBfree_result(db_events);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBimplode_exp                                                    *
 *                                                                            *
 * Purpose: implode normal mode expression to short mode                      *
 *          {hostX:keyY.functionZ(parameterN)}=1 implode to {11}=1            *
 *                                                                            *
 * Parameters: expression - null terminated string                            *
 *             triggerid - trigger identificator from database                *
 *                                                                            *
 * Return value: dynamically allocated memory for imploded expression         *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: function dynamically allocate memory, don't forget free them     *
 *           !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static char*	DBimplode_exp (
		char		*expression,
		zbx_uint64_t	triggerid
	)
{
	DB_RESULT	db_items;

	DB_ROW		item_data;

	zbx_uint64_t
		functionid,
		itemid;

	char	*exp = NULL;

	int
		host_len = 0,
		key_len = 0,
		function_len = 0,
		parameter_len = 0;

	char	*sql = NULL,
		*simple_start = NULL,
		*simple_end = NULL,
		*pos = NULL,
		*p = NULL,
		*host = NULL,
		*key = NULL,
		*function = NULL,
		*parameter = NULL,
		*str_esc = NULL;

	/* Translate localhost:procload.last(0)>10 to {12}>10 */

	for ( pos = expression; pos && *pos; )
	{
		if( NULL == (simple_start = strchr(pos, '{')) )	break;
		if( NULL == (simple_end = strchr(simple_start, '}')) )	break;

		host		= NULL;
		key		= NULL;
		function	= NULL;
		parameter	= NULL;

		host_len	= 0;
		key_len		= 0;
		function_len	= 0;
		parameter_len	= 0;

		*simple_start = '\0';
		exp = zbx_strdcat(exp, pos);
		*simple_start = '{';

		simple_start++;
		pos = simple_end+1;

		*simple_end = '\0';
		do { /* simple try for C */
			/* determine host */
			host = simple_start;
			if( NULL == (p = strchr(host, ':'))) break; /* simpla throw for C */
			host_len = p - host;

			/* determine parameter */
			if( NULL == (p = strrchr(p+1, '(')) ) break; /* simpla throw for C */
			parameter = p+1;

			if( NULL == (p = strrchr(parameter, ')')) ) break; /* simpla throw for C */
			parameter_len = p - parameter;

			/* determine key and function */
			p = parameter-1; /* position of '(' character */
			*p = '\0';
			do { /* simple try for C */
				key = host + host_len + 1;
				if( NULL == (p = strrchr(key, '.'))) break; /* simpla throw for C */
				key_len = p - key;

				function = p + 1;
				function_len = parameter - 1 - function;

			} while(0); /* simpla finally for C */
			*p = '(';

		} while(0);/* simpla finally for C */

		if( host && host_len && key && key_len && function && function_len && parameter && parameter_len )
		{
			sql = zbx_strdcat(NULL, "select distinct i.itemid from items i,hosts h where h.hostid=i.hostid ");
			/* adding host */
			host[host_len] = '\0';
			str_esc = DBdyn_escape_string(host);
			sql = zbx_strdcatf(sql, " and h.host='%s'", str_esc);
			zbx_free(str_esc);
			host[host_len] = ':';

			/* adding key */
			key[key_len] = '\0';
			str_esc = DBdyn_escape_string(key);
			sql = zbx_strdcatf(sql, " and i.key_='%s'", str_esc);
			zbx_free(str_esc);
			key[key_len] = '.';

			db_items = DBselect("%s",sql);

			zbx_free(sql);

			if( (item_data = DBfetch(db_items)) )
			{
				ZBX_STR2UINT64(itemid, item_data[0]);
				functionid = DBget_maxid("functions","functionid");

				sql = zbx_dsprintf(NULL, "insert into functions (functionid,itemid,triggerid,function,parameter)"
						" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ",",
						functionid, itemid, triggerid);
				/* adding function */
				function[function_len] = '\0';
				str_esc = DBdyn_escape_string(function);
				sql = zbx_strdcatf(sql, "'%s',", str_esc);
				zbx_free(str_esc);
				function[function_len] = '(';
				/* adding parameter */
				parameter[parameter_len] = '\0';
				str_esc = DBdyn_escape_string(parameter);
				sql = zbx_strdcatf(sql, "'%s')", str_esc);
				zbx_free(str_esc);
				parameter[parameter_len] = ')';

				DBexecute("%s",sql);

				exp = zbx_strdcatf(exp, "{" ZBX_FS_UI64 "}", functionid);

				zbx_free(sql);
			}

			DBfree_result(db_items);
		}
		else
		{
			exp = zbx_strdcatf(exp, "{%s}", simple_start);
		}
		*simple_end = '}';
	}

	if( pos )  exp = zbx_strdcat(exp, pos);

	return exp;
}

/******************************************************************************
 *                                                                            *
 * Function: DBget_trigger_dependences_by_triggerid                           *
 *                                                                            *
 * Purpose: retrive trigger dependences                                       *
 *                                                                            *
 * Parameters: triggerid - trigger identificator from database                *
 *             dependences - buffer for result                                *
 *             max_dependences - size of buffer in counts                     *
 *                                                                            *
 * Return value: count of dependences                                         *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBget_trigger_dependences_by_triggerid(
		zbx_uint64_t	triggerid,
		zbx_uint64_t	*dependences,
		int		max_dependences
	)
{
	DB_RESULT	db_triggers;

	DB_ROW		trigger_data;

	register int	i = 0;

	db_triggers = DBselect("select triggerid_up from trigger_depends where triggerid_down=" ZBX_FS_UI64, triggerid);

	while( i < (max_dependences - 1) && (trigger_data = DBfetch(db_triggers)) )
	{
		ZBX_STR2UINT64(dependences[i], trigger_data[0]);
		i++;
	}

	DBfree_result(db_triggers);

	dependences[i] = 0;

	return i;
}

/******************************************************************************
 *                                                                            *
 * Function: DBexplode_exp                                                    *
 *                                                                            *
 * Purpose: explode short trigger expression to normal mode                   *
 *          {11}=1 explode to {hostX:keyY.functionZ(parameterN)}=1            *
 *                                                                            *
 * Parameters: short_expression - null terminated string                      *
 *                                                                            *
 * Return value: dynamically allocated memory for exploded expression         *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: function dynamically allocate memory, don't forget free them     *
 *           !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 *                                                                            *
 ******************************************************************************/
static char* DBexplode_exp (char *short_expression)
{
	typedef enum {
		EXP_NONE,
		EXP_FUNCTIONID
	} expression_parsing_states;

	int state = EXP_NONE;

	DB_RESULT	db_functions;

	DB_ROW		function_data;

	char
		tmp_chr,	
		*p_functionid = NULL,
		*exp = NULL;

	int	p_functionid_len=0;

	zbx_uint64_t
		functionid;


	register char	*c;

	for( c=short_expression; c && *c; c++ )
	{
		if( '{' == *c )
		{
			state = EXP_FUNCTIONID;
			p_functionid = c+1;
			p_functionid_len = 0;
		}
		else if( '}' == *c && EXP_FUNCTIONID == state && p_functionid &&  p_functionid_len > 0)
		{
			state = EXP_NONE;

			tmp_chr = p_functionid[p_functionid_len];
			p_functionid[p_functionid_len] = '\0';

			if( 0 == strcmp("TRIGGER.VALUE", p_functionid))
			{
				exp = zbx_strdcatf(exp, "{%s}", p_functionid);
			}
			else
			{
				ZBX_STR2UINT64(functionid, p_functionid);

				db_functions = DBselect("select h.host,i.key_,f.function,f.parameter,i.itemid,i.value_type "
						" from items i,functions f,hosts h "
						" where functionid=" ZBX_FS_UI64 " and i.itemid=f.itemid and h.hostid=i.hostid",
						functionid);

				if( (function_data = DBfetch(db_functions)) )
				{
					exp = zbx_strdcatf(exp, "{%s:%s.%s(%s)}", function_data[0], 
							function_data[1], function_data[2], function_data[3]);
				}
				else
				{
					exp = zbx_strdcat(exp, "{*ERROR*}");
				}

				DBfree_result(db_functions);
			}

			p_functionid[p_functionid_len] = tmp_chr;
		}
		else if( EXP_FUNCTIONID == state )
		{
			p_functionid_len++;
		}
		else
		{
			exp = zbx_strdcatf(exp, "%c", *c);
		}
	}

	return exp;
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_trigger                                                 *
 *                                                                            *
 * Purpose: update trigger                                                    *
 *                                                                            *
 * Parameters: triggerid - trigger identificator from database                *
 *             expression - new expression (NULL to skip update)              *
 *             description - new description (NULL to skip update)            *
 *             priority - new priority (-1 to skip updete)                    *
 *             status - new status (-1 to skip updete)                        *
 *             comments - new comments (NULL to skip update)                  *
 *             url - new url (NULL to skip update)                            *
 *             dependences - null terminated array with dependences           *
 *             templateid - template trigger identificator from database      *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBupdate_trigger(
		zbx_uint64_t	triggerid,
		char		*expression,
		const char	*description,
		int		priority,
		int		status,
		const char	*comments,
		const char	*url,
		int		type,
		zbx_uint64_t	*dependences,
		zbx_uint64_t	templateid
	)
{
	DB_RESULT	db_triggers;
	DB_RESULT	db_chd_triggers;
	DB_RESULT	db_chd_hosts;

	DB_ROW		trigger_data;
	DB_ROW		chd_trigger_data;
	DB_ROW		chd_host_data;

	zbx_uint64_t
		chd_hostid,
		chd_triggerid,
		new_dependences[ZBX_MAX_DEPENDENCES];

	char 
		*search = NULL,
		*replace = NULL,
		*new_expression = NULL,
		*exp_expression = NULL,
		*short_expression = NULL,
		*sql = NULL,
		*str_esc = NULL;

	int	i = 0;

	db_triggers = DBselect("select distinct t.description,h.host,t.expression,t.priority,t.status,t.comments,t.url,t.type "
		       " from triggers t,functions f,items i,hosts h "
		       " where t.triggerid=" ZBX_FS_UI64 " and f.triggerid=t.triggerid "
		       " and i.itemid=f.itemid and i.hostid=h.hostid", triggerid);

	if( (trigger_data = DBfetch(db_triggers)) )
	{
		if( !expression )	expression = exp_expression = DBexplode_exp(trigger_data[2]);
		if( !description )	description = trigger_data[0];
		if( -1 == priority )	priority = atoi(trigger_data[3]);
		if( -1 == status )	priority = atoi(trigger_data[4]);
		if( !comments )		comments = trigger_data[5];
		if( !url )		url = trigger_data[6];
		if( -1 == type )	type = atoi(trigger_data[7]);

		search = zbx_dsprintf(search, "{%s:", trigger_data[1] /* template host */);

		db_chd_triggers = DBselect("select distinct triggerid from triggers where templateid=" ZBX_FS_UI64, triggerid);

		while( (chd_trigger_data = DBfetch(db_chd_triggers)) )
		{
			ZBX_STR2UINT64(chd_triggerid, chd_trigger_data[0]);

			db_chd_hosts = DBselect("select distinct h.hostid,h.host from hosts h,items i,functions f "
					" where f.triggerid=" ZBX_FS_UI64 " and f.itemid=i.itemid and i.hostid=h.hostid");

			if( (chd_host_data = DBfetch(db_chd_hosts)) )
			{
				ZBX_STR2UINT64(chd_hostid, chd_host_data[0]);

				replace = zbx_dsprintf(replace, "{%s:", chd_host_data[0] /* child host */);

				new_expression = string_replace(expression, search, replace);

				zbx_free(replace);

				DBreplace_template_dependences(dependences, chd_hostid, new_dependences, sizeof(new_dependences) / sizeof(zbx_uint64_t));

				/* recursion */
				DBupdate_trigger(
					chd_triggerid,
					new_expression,
					description,
					priority,
					-1,           /* status */
					comments,
					url,
					type,
					new_dependences,
					triggerid);

				zbx_free(new_expression);				
			}

			DBfree_result(db_chd_hosts);
		}
		zbx_free(search);

		DBfree_result(db_chd_triggers);

		DBexecute("delete from functions where triggerid=" ZBX_FS_UI64, triggerid);

		sql = zbx_strdcat(NULL, "update triggers set");

		if( expression ) {
			short_expression = DBimplode_exp(expression, triggerid);
			str_esc = DBdyn_escape_string(short_expression);
			sql = zbx_strdcatf(sql, " expression='%s',", str_esc);
			zbx_free(str_esc);
			zbx_free(short_expression);

		}
		if( description ) {
			str_esc = DBdyn_escape_string(description);
			sql = zbx_strdcatf(sql, " description='%s',", str_esc);
			zbx_free(str_esc);
		}
		if( priority >= 0 )	sql = zbx_strdcatf(sql, " priority=%i,", priority);
		if( status >= 0 )	sql = zbx_strdcatf(sql, " status=%i,", status);
		if( comments )
		{
			str_esc = DBdyn_escape_string(comments);
			sql = zbx_strdcatf(sql, " comments='%s',", str_esc);
			zbx_free(str_esc);
		}
		if( url ) {
			str_esc = DBdyn_escape_string(url);
			sql = zbx_strdcatf(sql, " url='%s',", str_esc);
			zbx_free(str_esc);
		}
		if( templateid )	sql = zbx_strdcatf(sql, " templateid=" ZBX_FS_UI64 ",", templateid);

		if( type >= 0 )		sql = zbx_strdcatf(sql, " type=%i,", type);

		sql = zbx_strdcatf(sql, " value=2 where triggerid=" ZBX_FS_UI64,	triggerid);

		DBexecute("%s",sql);

		zbx_free(sql);

		DBreset_items_nextcheck(triggerid);

		DBadd_event(triggerid,TRIGGER_VALUE_UNKNOWN,0);

		DBdelete_dependencies_by_triggerid(triggerid);

		for( i=0; 0 < dependences[i]; i++ )
		{
			DBinsert_dependency(triggerid, dependences[i]);
		}

		zbx_free(exp_expression);

		zabbix_log(LOG_LEVEL_DEBUG, "Trigger '%s' updated", trigger_data[0]);;
	}

	DBfree_result(db_triggers);

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcmp_triggers                                                   *
 *                                                                            *
 * Purpose: compare two triggers                                              *
 *                                                                            *
 * Parameters: triggerid1 - first trigger identificator from database         *
 *             triggerid2 - second trigger identificator from database        *
 *                                                                            *
 * Return value: 0 - if triggers coincide                                     *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcmp_triggers(
		zbx_uint64_t triggerid1,
		zbx_uint64_t triggerid2
	)
{
	DB_RESULT	db_trigers1;
	DB_RESULT	db_trigers2;
	DB_RESULT	db_functions;

	DB_ROW		trigger1_data;
	DB_ROW		trigger2_data;
	DB_ROW		function_data;

	char
		*search = NULL,
		*replace = NULL,
		*old_expr = NULL,
		*expr = NULL;

	int	result = 1;

	db_trigers1 = DBselect("select expression from triggers where triggerid=" ZBX_FS_UI64, triggerid1);

	if( (trigger1_data = DBfetch(db_trigers1)) )
	{
		db_trigers2 = DBselect("select expression from triggers where triggerid=" ZBX_FS_UI64, triggerid2);

		if( (trigger2_data = DBfetch(db_trigers2)) )
		{
			expr = strdup(trigger2_data[0]);

			db_functions = DBselect("select f1.functionid,f2.functionid from functions f1,functions f2,items i1,items i2 "
			" where f1.function=f2.function and f1.parameter=f2.parameter and i1.key_=i2.key_ "
			" and i1.itemid=f1.itemid and i2.itemid=f2.itemid and f1.triggerid=" ZBX_FS_UI64 " and f2.triggerid=" ZBX_FS_UI64,
				triggerid1, triggerid2);

			while( (function_data = DBfetch(db_functions)) )
			{
				search = zbx_dsprintf(NULL, "{%s}", function_data[1]);
				replace = zbx_dsprintf(NULL, "{%s}", function_data[0]);

				old_expr = expr;
				expr = string_replace(old_expr, search, replace);
				zbx_free(old_expr);

				zbx_free(replace);
				zbx_free(search);
			}

			DBfree_result(db_functions);

			result = strcmp(trigger1_data[0], expr);

			zbx_free(expr);
		}

		DBfree_result(db_trigers2);
	}

	DBfree_result(db_trigers1);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_trigger_to_host                                           *
 *                                                                            *
 * Purpose: copy specified trigger to host                                    *
 *                                                                            *
 * Parameters: triggerid - trigger identificator from database                *
 *             hostid - host identificator from database                      *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_trigger_to_host(
		zbx_uint64_t triggerid,
		zbx_uint64_t hostid,
		unsigned char copy_mode
	)
{
	DB_RESULT	db_triggers;
	DB_RESULT	db_host_triggers;
	DB_RESULT	db_chd_hosts;
	DB_RESULT	db_functions;
	DB_RESULT	db_items;

	DB_ROW		trigger_data;
	DB_ROW		host_trigger_data;
	DB_ROW		chd_host_data;
	DB_ROW		function_data;
	DB_ROW		item_data;

	zbx_uint64_t
		itemid,
		h_triggerid,
		chd_hostid,
		functionid,
		new_triggerid,
		new_functionid,
		dependences[ZBX_MAX_DEPENDENCES],
		new_dependences[ZBX_MAX_DEPENDENCES];

	char
		*old_expression = NULL,
		*new_expression = NULL,
		*new_expression_esc = NULL,
		*search = NULL,
		*replace = NULL,
		*description_esc = NULL,
		*comments_esc = NULL,
		*url_esc = NULL,
		*function_esc = NULL,
		*parameter_esc = NULL;

	int	i = 0;

	int	result = SUCCEED;

	db_triggers = DBselect("select description,priority,status,comments,url,expression,type from triggers where triggerid=" ZBX_FS_UI64, triggerid);

	if( (trigger_data = DBfetch(db_triggers)) )
	{
		result = FAIL;

		DBget_trigger_dependences_by_triggerid(triggerid, dependences, sizeof(dependences) / sizeof(zbx_uint64_t));
		DBreplace_template_dependences(dependences, hostid, new_dependences, sizeof(new_dependences) / sizeof(zbx_uint64_t));

		db_host_triggers = DBselect("select distinct t.triggerid,t.templateid from functions f,items i,triggers t "
				" where t.templateid=0 and t.triggerid=f.triggerid and i.itemid=f.itemid and i.hostid=" ZBX_FS_UI64, hostid);

		while( (host_trigger_data = DBfetch(db_host_triggers)) )
		{
			ZBX_STR2UINT64(h_triggerid, host_trigger_data[0]);

			if( DBcmp_triggers(triggerid, h_triggerid) )	continue;

			/* link not linked trigger with same expression */
			result = DBupdate_trigger(
				h_triggerid,
				NULL,			/* expression */
				trigger_data[0],	/* description */
				atoi(trigger_data[1]),	/* priority */
				-1,			/* status */
				trigger_data[3],	/* comments */
				trigger_data[4],	/* url */
				atoi(trigger_data[6]),	/* type */
				new_dependences,
				copy_mode ? 0 : triggerid);

			break;
		}

		DBfree_result(db_host_triggers);

		if( SUCCEED != result )
		{ /* create triger if no updated triggers */
			result = SUCCEED;

			new_triggerid = DBget_maxid("triggers","triggerid");

			description_esc = DBdyn_escape_string(trigger_data[0]);
			comments_esc = DBdyn_escape_string(trigger_data[3]);
			url_esc = DBdyn_escape_string(trigger_data[4]);

			DBexecute("insert into triggers"
				" (triggerid,description,priority,status,comments,url,type,value,expression,templateid)"
				" values (" ZBX_FS_UI64 ",'%s',%i,%i,'%s','%s',%i,2,'0'," ZBX_FS_UI64 ")",
					new_triggerid,
					description_esc,	/* description */
					atoi(trigger_data[1]),	/* priority */
					atoi(trigger_data[2]),	/* status */
					comments_esc,		/* comments */
					url_esc,		/* url */
					atoi(trigger_data[6]),	/* type */
					copy_mode ? 0 : triggerid);

			zbx_free(url_esc);
			zbx_free(comments_esc);
			zbx_free(description_esc);

			new_expression = strdup(trigger_data[5]);

			/* Loop: functions */
			db_functions = DBselect("select itemid,function,parameter,functionid from functions "
						" where triggerid=" ZBX_FS_UI64, triggerid);
/* TODO: out of memory!!! */
			while( SUCCEED == result && (function_data = DBfetch(db_functions)) )
			{
				ZBX_STR2UINT64(itemid, function_data[0]);
				ZBX_STR2UINT64(functionid, function_data[3]);

				function_esc = DBdyn_escape_string(function_data[1]);
				parameter_esc = DBdyn_escape_string(function_data[2]);

				search = zbx_dsprintf(NULL, "{" ZBX_FS_UI64 "}", functionid);

				db_items = DBselect("select i2.itemid from items i1, items i2 "
						" where i2.key_=i1.key_ and i2.hostid=" ZBX_FS_UI64
						" and i1.itemid=" ZBX_FS_UI64, hostid, itemid);
				
				if( (item_data = DBfetch(db_items)) )
				{
					ZBX_STR2UINT64(itemid, item_data[0]);

					new_functionid = DBget_maxid("functions","functionid");

					replace = zbx_dsprintf(NULL, "{" ZBX_FS_UI64 "}", new_functionid);

					DBexecute("insert into functions (functionid,itemid,triggerid,function,parameter)"
						" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ",'%s','%s')",
								new_functionid,
								itemid,
								new_triggerid,
								function_esc,
								parameter_esc
							);

					old_expression = new_expression;
					new_expression = string_replace(old_expression, search, replace);

					zbx_free(old_expression);
					zbx_free(replace);
				}
				else
				{
					zabbix_log(LOG_LEVEL_DEBUG, "Missing similar key [" ZBX_FS_UI64 "] for host [" ZBX_FS_UI64 "]",
							itemid, hostid);
					result = FAIL;
				}

				DBfree_result(db_items);

				zbx_free(search);

				zbx_free(parameter_esc);
				zbx_free(function_esc);

			}

			DBfree_result(db_functions);

			if( SUCCEED == result )
			{
				new_expression_esc = DBdyn_escape_string(new_expression);
				DBexecute("update triggers set expression='%s' where triggerid=" ZBX_FS_UI64, new_expression, new_triggerid);
				zbx_free(new_expression_esc);

				/* copy dependences */
				DBdelete_dependencies_by_triggerid(new_triggerid);
				for( i=0; 0 < new_dependences[i]; i++ )
				{
					DBinsert_dependency(new_triggerid, new_dependences[i]);
				}

				zabbix_log(LOG_LEVEL_DEBUG, "Added trigger '%s' to host [" ZBX_FS_UI64 "]", trigger_data[0], hostid);

				/* Copy triggers to the child hosts */
				db_chd_hosts = DBselect("select hostid from hosts_templates where templateid=" ZBX_FS_UI64, hostid);

				while( (chd_host_data = DBfetch(db_chd_hosts)) )
				{
					ZBX_STR2UINT64(chd_hostid, chd_host_data[0]);

					/* recursion */
					if( SUCCEED != (result = DBcopy_trigger_to_host(new_triggerid, chd_hostid, copy_mode)) )
						break;
				}

				DBfree_result(db_chd_hosts);
			}
			zbx_free(new_expression);

		}
	}

	DBfree_result(db_triggers);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBupdate_template_dependences_for_host                           *
 *                                                                            *
 * Purpose: update trigger dependences for specified host                     *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBupdate_template_dependences_for_host(
		zbx_uint64_t hostid
	)
{
	DB_RESULT	db_triggers;
	DB_RESULT	db_chd_triggers;

	DB_ROW		trigger_data;
	DB_ROW		chd_trigger_data;

	int	result = SUCCEED;

	zbx_uint64_t
		triggerid,
		chd_triggerid,
		dependences[ZBX_MAX_DEPENDENCES],
		new_dependences[ZBX_MAX_DEPENDENCES];

	db_triggers = DBselect("select distinct t.triggerid from triggers t, functions f, items i"
		" where i.hostid=" ZBX_FS_UI64 " and f.itemid=i.itemid and f.triggerid=t.triggerid", hostid);

	while( (trigger_data = DBfetch(db_triggers)) )
	{
		ZBX_STR2UINT64(triggerid, trigger_data[0]);

		db_chd_triggers = DBselect("select distinct triggerid from triggers where templateid=" ZBX_FS_UI64, triggerid);

		while( (chd_trigger_data = DBfetch(db_chd_triggers)) )
		{
			ZBX_STR2UINT64(chd_triggerid, chd_trigger_data[0]);

			DBget_trigger_dependences_by_triggerid(triggerid, dependences, sizeof(dependences) / sizeof(zbx_uint64_t));
			DBreplace_template_dependences(dependences, hostid, new_dependences, sizeof(new_dependences) / sizeof(zbx_uint64_t));

			if( SUCCEED != (result = DBupdate_trigger(
				chd_triggerid,
				/* expression */         NULL,
				/* description */        NULL,
				/* priority */           -1,
				/* status */             -1,
				/* comments */           NULL,
				/* url */                NULL,
				/* type */               -1,
				new_dependences,
				triggerid)) )
					break;
		}

		DBfree_result(db_chd_triggers);

		if( SUCCEED != result ) break;
	}

	DBfree_result(db_triggers);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_template_triggers                                         *
 *                                                                            *
 * Purpose: Copy template triggers to host                                    *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_template_triggers(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char copy_mode
	)
{
	DB_RESULT	db_elements;

	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	int	result = SUCCEED;

	if( 0 == templateid)
	{ /* sync with all linkage templates */
		db_elements = DBselect("select templateid from hosts_templates where hostid=" ZBX_FS_UI64, hostid);

		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( 0 == elementid ) continue;

			/* recursion */
			if( SUCCEED != (result = DBcopy_template_triggers(hostid, elementid, copy_mode)) )
				break;
		}

		DBfree_result(db_elements);
	}
	else
	{
		db_elements = DBselect("select distinct t.triggerid from triggers t, functions f, items i"
		                        " where i.hostid=" ZBX_FS_UI64 " and f.itemid=i.itemid and f.triggerid=t.triggerid", templateid);
		
		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);

			if( SUCCEED != (result = DBcopy_trigger_to_host(elementid, hostid, copy_mode)) )
				break;
		}

		DBfree_result(db_elements);

		if( SUCCEED == result)
			result = DBupdate_template_dependences_for_host(hostid);
	}

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_graph_to_host                                             *
 *                                                                            *
 * Purpose: copy specified graph to host                                      *
 *                                                                            *
 * Parameters: graphid - graph identificator from database                    *
 *             hostid - host identificator from database                      *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_graph_to_host(
		zbx_uint64_t	graphid,
		zbx_uint64_t	hostid,
		unsigned char copy_mode
	)
{
	DB_RESULT	db_items;
	DB_ROW		db_item_data;

	DB_RESULT	db_graphs;
	DB_ROW		db_graph_data;

	DB_RESULT	chd_graphs;
	DB_ROW		chd_graph_data;

	ZBX_GRAPH_ITEMS chd_gitem;
	ZBX_GRAPH_ITEMS *gitems = NULL;
	ZBX_GRAPH_ITEMS *new_gitems = NULL;

	zbx_uint64_t	chd_graphid = 0;
	zbx_uint64_t	chd_templateid = 0;

	int	gitems_count = 0,
		i = 0,
		gitem_equal = 0,
		equal = 0;

	int	result = SUCCEED;

	result = FAIL;

	gitems = (ZBX_GRAPH_ITEMS*)zbx_malloc((void*)gitems, sizeof(ZBX_GRAPH_ITEMS));
	gitems[0].itemid = 0;
	gitems_count = 0;

	db_items = DBselect("select gi.itemid,gi.color,gi.drawtype,gi.sortorder,gi.yaxisside,gi.calc_fnc,gi.type,gi.periods_cnt "
			"from graphs_items gi where gi.graphid=" ZBX_FS_UI64, graphid);
	while ( (db_item_data = DBfetch(db_items)) )
	{
		ZBX_STR2UINT64(gitems[gitems_count].itemid, db_item_data[0]);

		gitems[gitems_count].color	= strdup(db_item_data[1]);
		gitems[gitems_count].drawtype	= atoi(db_item_data[2]);
		gitems[gitems_count].sortorder	= atoi(db_item_data[3]);
		gitems[gitems_count].yaxisside	= atoi(db_item_data[4]);
		gitems[gitems_count].calc_fnc	= atoi(db_item_data[5]);
		gitems[gitems_count].type	= atoi(db_item_data[6]);
		gitems[gitems_count].periods_cnt= atoi(db_item_data[7]);

		gitems = zbx_realloc(gitems, (gitems_count+2)*sizeof(ZBX_GRAPH_ITEMS));
		gitems[gitems_count+1].itemid = 0;

		gitems_count++;
	}
	DBfree_result(db_items);

	db_graphs = DBselect("select name,width,height,yaxistype,yaxismin,yaxismax,show_work_period,"
			"show_triggers,graphtype,show_legend,show_3d from graphs where graphid=" ZBX_FS_UI64, graphid);

	db_graph_data = DBfetch(db_graphs);

	if ( (new_gitems = DBget_same_graphitems_for_host(gitems, hostid)) )
	{
		chd_graphid = 0;
		chd_graphs = DBselect("select distinct g.graphid,g.name,g.width,g.height,g.yaxistype,g.yaxismin,g.yaxismax,g.show_work_period,"
				"g.show_triggers,g.graphtype,g.show_legend,g.show_3d,g.templateid from graphs g, graphs_items gi, items i "
				" where g.graphid=gi.graphid and gi.itemid=i.itemid and i.hostid=" ZBX_FS_UI64, hostid);
		while( !chd_graphid && (chd_graph_data = DBfetch(chd_graphs)))
		{ /* compare graphs */
			ZBX_STR2UINT64(chd_graphid, chd_graph_data[0]);
			ZBX_STR2UINT64(chd_templateid, chd_graph_data[12]);

			if ( chd_templateid != 0 ) continue;

			equal = 0;
			db_items = DBselect("select gi.itemid,gi.color,gi.drawtype,gi.sortorder,"
					"gi.yaxisside,gi.calc_fnc,gi.type,gi.periods_cnt "
					" from graphs_items gi where gi.graphid=" ZBX_FS_UI64, graphid);
			while( (db_item_data = DBfetch(db_items)) )
			{
				ZBX_STR2UINT64(chd_gitem.itemid, db_item_data[0]);

				chd_gitem.color		= db_item_data[1]; /* NOTE: copy refernce only */
				chd_gitem.drawtype	= atoi(db_item_data[2]);
				chd_gitem.sortorder	= atoi(db_item_data[3]);
				chd_gitem.yaxisside	= atoi(db_item_data[4]);
				chd_gitem.calc_fnc	= atoi(db_item_data[5]);
				chd_gitem.type		= atoi(db_item_data[6]);
				chd_gitem.periods_cnt	= atoi(db_item_data[7]);

				gitem_equal = 0;
				for ( i = 0; new_gitems[i].itemid != 0; i++ )
				{
					if(DBcmp_graphitems(&new_gitems[i], &chd_gitem))	continue;

					gitem_equal = 1;
					break;
				}

				if ( !gitem_equal )
				{
					equal = 0;
					break;
				}

				/* founded equal graph item */
				equal++;
			}

			DBfree_result(db_items);

			if ( equal && gitems_count == equal )
			{ /* founded equal graph */
				break;
			}

			chd_graphid = 0;
		}

		DBfree_result(chd_graphs);

		if ( chd_graphid )
		{
			result = DBupdate_graph_with_items(
				chd_graphid,
				db_graph_data[0],		/* name */
				atoi(db_graph_data[1]),	/* width */
				atoi(db_graph_data[2]),	/* height */
				atoi(db_graph_data[3]),	/* yaxistype */
				atoi(db_graph_data[4]),	/* yaxismin */
				atoi(db_graph_data[5]),	/* yaxismax */
				atoi(db_graph_data[6]),	/* show_work_period */
				atoi(db_graph_data[7]),	/* show_triggers */
				atoi(db_graph_data[8]),	/* graphtype */
				atoi(db_graph_data[9]),	/* show_legend */
				atoi(db_graph_data[10]),/* show_3d */
				new_gitems,
				copy_mode ? 0 : graphid);
		}
		else
		{
			result = DBadd_graph_with_items(
				&chd_graphid,
				db_graph_data[0],	/* name */
				atoi(db_graph_data[1]),	/* width */
				atoi(db_graph_data[2]),	/* height */
				atoi(db_graph_data[3]),	/* yaxistype */
				atoi(db_graph_data[4]),	/* yaxismin */
				atoi(db_graph_data[5]),	/* yaxismax */
				atoi(db_graph_data[6]),	/* show_work_period */
				atoi(db_graph_data[7]),	/* show_triggers */
				atoi(db_graph_data[8]),	/* graphtype */
				atoi(db_graph_data[9]),	/* show_legend */
				atoi(db_graph_data[10]),/* show_3d */
				new_gitems,
				copy_mode ? 0 : graphid);
		}

		zbx_free_gitems(new_gitems);
	}
	else
	{
		zabbix_log(LOG_LEVEL_DEBUG, "Skipped coping of graph '%s' to host " ZBX_FS_UI64, db_graph_data[0], hostid);
		result = FAIL;
	}

	zbx_free_gitems(gitems);
	DBfree_result(db_graphs);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_template_graphs                                           *
 *                                                                            *
 * Purpose: copy graphs from template to host                                 *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBcopy_template_graphs(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char copy_mode
	)
{
	DB_RESULT	db_elements;

	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	int	result = SUCCEED;

	if( 0 == templateid)
	{ /* sync with all linkage templates */
		db_elements = DBselect("select templateid from hosts_templates where hostid=" ZBX_FS_UI64, hostid);

		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);
			if( 0 == elementid ) continue;

			/* recursion */
			if( SUCCEED != (result = DBcopy_template_triggers(hostid, elementid, copy_mode)) )
				break;
		}

		DBfree_result(db_elements);
	}
	else
	{
		db_elements = DBselect("select distinct g.graphid from graphs g, graphs_items gi, items i"
				" where g.graphid=gi.graphid and gi.itemid=i.itemid and i.hostid=" ZBX_FS_UI64, templateid);
		
		while( (element_data = DBfetch(db_elements)) )
		{
			ZBX_STR2UINT64(elementid, element_data[0]);

			if( SUCCEED != (result = DBcopy_graph_to_host(elementid, hostid, copy_mode)) )
				break;
		}

		DBfree_result(db_elements);
	}

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_sysmaps_elements_with_hostid                            *
 *                                                                            *
 * Purpose: delete hosts from maps                                            *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
static int	DBdelete_sysmaps_elements_with_hostid(
		zbx_uint64_t	hostid
	)
{
	DB_RESULT	db_elements;

	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	db_elements = DBselect("select selementid from sysmaps_elements"
		" where elementid=" ZBX_FS_UI64 " and elementtype=%i", hostid, SYSMAP_ELEMENT_TYPE_HOST);

	while( (element_data = DBfetch(db_elements)) )
	{
		ZBX_STR2UINT64(elementid, element_data[0]);

		DBdelete_sysmaps_element(elementid);
	}

	DBfree_result(db_elements);

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Function: DBdelete_template_elements                                       *
 *                                                                            *
 * Purpose: delete template elements from host                                *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             unlink_mode - 1 - only unlink elements without deletion        *
 *                           0 - delete elements                              *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
/* public */ void	DBdelete_template_elements(
		zbx_uint64_t  hostid,
		zbx_uint64_t templateid,
		unsigned char unlink_mode
	)
{
	DBdelete_template_graphs(hostid, templateid, unlink_mode);
	DBdelete_template_triggers(hostid, templateid, unlink_mode);
	DBdelete_template_items(hostid, templateid, unlink_mode);
	DBdelete_template_applications(hostid, templateid, unlink_mode);
}

/******************************************************************************
 *                                                                            *
 * Function: DBcopy_template_elements                                         *
 *                                                                            *
 * Purpose: copy elements from specified template                             *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *             copy_mode - 1 - only copy elements without linkage             *
 *             	           0 - copy and link elements                         *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
/* public */ int	DBcopy_template_elements(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid,
		unsigned char copy_mode
	)
{
	int result = SUCCEED;

	if(SUCCEED == (result = DBcopy_template_applications(hostid, templateid, copy_mode)) )
	if(SUCCEED == (result = DBcopy_template_items(hostid, templateid, copy_mode)) )
	if(SUCCEED == (result = DBcopy_template_triggers(hostid, templateid, copy_mode)) )
		result = DBcopy_template_graphs(hostid, templateid, copy_mode);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBsync_host_with_template                                        *
 *                                                                            *
 * Purpose: synchronize elements from specified template                      *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
/* public */ int	DBsync_host_with_template(
		zbx_uint64_t hostid,
		zbx_uint64_t templateid
	)
{
	DBdelete_template_elements(hostid, templateid, 0 /* not a unlink mode */);

	return DBcopy_template_elements(hostid, templateid, 0 /* not a copy mode */);
}

/******************************************************************************
 *                                                                            *
 * Function: DBsync_host_with_templates                                       *
 *                                                                            *
 * Purpose: synchronize elements from linked templates                        *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *                                                                            *
 * Return value: upon successful completion return SUCCEED                    *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
/* public */ int	DBsync_host_with_templates(
		zbx_uint64_t hostid
	)
{
	DB_RESULT	db_templates;

	DB_ROW		template_data;

	zbx_uint64_t
		triggerid;

	int	result = SUCCEED;

	zabbix_log( LOG_LEVEL_DEBUG, "In DBsync_host_with_templates(%d)", hostid);

	db_templates = DBselect("select templateid,items,triggers,graphs from hosts_templates where hostid=" ZBX_FS_UI64, hostid);

	while( (template_data = DBfetch(db_templates)) )
	{
		ZBX_STR2UINT64(triggerid, template_data[0]);

		if( SUCCEED != (result = DBsync_host_with_template(hostid, triggerid)) )
			break;
	}

	DBfree_result(db_templates);

	return result;
}

/******************************************************************************
 *                                                                            *
 * Function: DBunlink_template                                                *
 *                                                                            *
 * Purpose: unlink template from host without element deletion                *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *             templateid - template identificator from database              *
 *                                                                            *
 * Return value:                                                              *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
/* public */ void	DBunlink_template(
		zbx_uint64_t	hostid,
	       	zbx_uint64_t	templateid
	)
{
	DBdelete_template_elements(hostid, templateid, 1 /* unlink, not delete */);

	DBexecute("delete from hosts_templates where hostid=" ZBX_FS_UI64 " and templateid=" ZBX_FS_UI64,
			hostid, templateid);
}


/******************************************************************************
 *                                                                            *
 * Function: DBdelete_host                                                    *
 *                                                                            *
 * Purpose: delete host from databases with all elements                      *
 *                                                                            *
 * Parameters: hostid - host identificator from database                      *
 *                                                                            *
 * Return value: always SUCCEED                                               *
 *                                                                            *
 * Author: Eugene Grigorjev                                                   *
 *                                                                            *
 * Comments: !!! Don't forget sync code with PHP !!!                          *
 *                                                                            *
 ******************************************************************************/
/* public */ int	DBdelete_host(
		zbx_uint64_t hostid
	)
{
	DB_RESULT	db_elements;

	DB_ROW		element_data;

	zbx_uint64_t
		elementid;

	/* unlink child hosts */
	db_elements = DBselect("select hostid from hosts_templates where templateid=" ZBX_FS_UI64, hostid);

	while( (element_data = DBfetch(db_elements)) )
	{
		ZBX_STR2UINT64(elementid, element_data[0]);
		DBunlink_template(elementid, hostid);
	}

	DBfree_result(db_elements);

	/* delete items -> triggers -> graphs */
	db_elements = DBselect("select itemid from items where hostid=" ZBX_FS_UI64, hostid);

	while( (element_data = DBfetch(db_elements)) )
	{
		ZBX_STR2UINT64(elementid, element_data[0]);

		DBdelete_item(elementid);
	}

	DBfree_result(db_elements);

	/* delete host from maps */
	DBdelete_sysmaps_elements_with_hostid(hostid);

	/* delete host from group */
	DBexecute("delete from hosts_groups where hostid=" ZBX_FS_UI64, hostid);

	/* delete host from template linkages */
	DBexecute("delete from hosts_templates where hostid=" ZBX_FS_UI64, hostid);

	/* disable actions */
	db_elements = DBselect("select distinct actionid from conditions "
		" where conditiontype=%i and value=" ZBX_FS_UI64,CONDITION_TYPE_HOST, hostid);

	while( (element_data = DBfetch(db_elements)) )
	{
		ZBX_STR2UINT64(elementid, element_data[0]);

		DBexecute("update actions set status=%s where actionid=" ZBX_FS_UI64, 
			ACTION_STATUS_DISABLED, elementid);
	}

	DBfree_result(db_elements);

	/* delete action conditions */
	DBexecute("delete from conditions where conditiontype=%i and value=" ZBX_FS_UI64, CONDITION_TYPE_HOST, hostid);

	/* delete host profile */
	DBexecute("delete from hosts_profiles where hostid=" ZBX_FS_UI64, hostid);

	/* delete host */
	DBexecute("delete from hosts where hostid=" ZBX_FS_UI64, hostid);

	return SUCCEED;
}
