/* 
 * parser.c
 * 
 * Copyright (c) 2018-2019 Eric Vidal <eric@obarun.org>
 * 
 * All rights reserved.
 * 
 * This file is part of Obarun. It is subject to the license terms in
 * the LICENSE file found in the top-level directory of this
 * distribution.
 * This file may not be copied, modified, propagated, or distributed
 * except according to the terms contained in the LICENSE file./
 */

#include <66/parser.h>

#include <string.h>
//#include <stdio.h>

#include <oblibs/string.h>
#include <oblibs/types.h>
#include <oblibs/directory.h>
#include <oblibs/error2.h>
#include <oblibs/sastr.h>

#include <skalibs/stralloc.h>

#include <66/resolve.h>
#include <66/utils.h>
#include <66/constants.h>
#include <66/environ.h>

int parse_service_check_enabled(ssexec_t *info, char const *svname,uint8_t force,uint8_t *exist)
{
	stralloc sares = STRALLOC_ZERO ;
	ss_resolve_t res = RESOLVE_ZERO ;
	int ret = 1 ;
	if (!ss_resolve_pointo(&sares,info,SS_NOTYPE,SS_RESOLVE_SRC))
	{
		VERBO3 strerr_warnwu1sys("set revolve pointer to source") ;
		goto err ;	
	} 
	if (ss_resolve_check(sares.s,svname))
	{
		if (!ss_resolve_read(&res,sares.s,svname)) 
		{
			VERBO3 strerr_warnwu2sys("read resolve file of: ",svname) ;
			goto err ;
		}
		if (res.disen)
		{
			(*exist) = 1 ;
			if (!force) { 
				VERBO1 strerr_warnw3x("Ignoring: ",svname," service: already enabled") ;
				ret = 2 ;
				goto freed ;
			}
		}
	}
	freed:
	stralloc_free(&sares) ;
	ss_resolve_free(&res) ;
	return ret ;
	err:
		stralloc_free(&sares) ;
		ss_resolve_free(&res) ;
		return 0 ;
}

int parse_add_service(stralloc *parsed_list,sv_alltype *sv_before,char const *service,unsigned int *nbsv,uid_t owner)
{
	stralloc conf = STRALLOC_ZERO ;
	size_t svlen = strlen(service) ;
	// keep source of the frontend file
	sv_before->src = keep.len ;
	if (!stralloc_catb(&keep,service,svlen + 1)) goto err ;
	// keep source of the configuration file
	if (sv_before->opts[2])
	{
		if (!env_resolve_conf(&conf,owner)) goto err ;
		sv_before->srconf = keep.len ;
		if (!stralloc_catb(&keep,conf.s,conf.len + 1)) goto err ;
	}
	// keep service on current list
	if (!stralloc_catb(parsed_list,service,svlen + 1)) goto err ;
	if (!genalloc_append(sv_alltype,&gasv,sv_before)) goto err ;
	(*nbsv)++ ;
	stralloc_free(&conf) ;
	return 1 ;
	err:
		stralloc_free(&conf) ;
		return 0 ;
}

int parse_service_deps(ssexec_t *info,stralloc *parsed_list, sv_alltype *sv_before, char const *sv,unsigned int *nbsv,stralloc *sasv,uint8_t force)
{
	uint8_t exist = 0 ;
	char *dname = 0 ;
	stralloc newsv = STRALLOC_ZERO ;
	if (sv_before->cname.nga)
	{
		size_t id = sv_before->cname.idga, nid = sv_before->cname.nga ;
		for (;nid; id += strlen(deps.s + id) + 1, nid--)
		{
			newsv.len = 0 ;
			if (sv_before->cname.itype != BUNDLE)
			{
				VERBO3 strerr_warni4x("Service : ",sv, " depends on : ",deps.s+id) ;
			}else VERBO3 strerr_warni5x("Bundle : ",sv, " contents : ",deps.s+id," as service") ;
			dname = deps.s + id ;
			if (!ss_resolve_src_path(&newsv,dname,info))
			{
				VERBO3 strerr_warnwu2x("resolve source path of: ",dname) ;
				goto err ;
			}
			if (!parse_service_before(info,parsed_list,newsv.s,nbsv,sasv,force,&exist)) goto err ;
		}
	}
	else VERBO3 strerr_warni2x(sv,": haven't dependencies") ;
	stralloc_free(&newsv) ;
	return 1 ;
	err:
		stralloc_free(&newsv) ;
		return 0 ;
}

int parse_service_before(ssexec_t *info,stralloc *parsed_list, char const *sv,unsigned int *nbsv, stralloc *sasv,uint8_t force,uint8_t *exist)
{
	
	int r, insta ;
	size_t svlen = strlen(sv), svsrclen, svnamelen ;
	char svname[svlen + 1], svsrc[svlen + 1], svpath[svlen + 1] ;
	if (!basename(svname,sv)) return 0 ;
	if (!dirname(svsrc,sv)) return 0 ;
	svsrclen = strlen(svsrc) ;
	svnamelen = strlen(svname) ;
	if (scan_mode(sv,S_IFDIR) == 1) return 1 ;
		
	r = parse_service_check_enabled(info,svname,force,exist) ;
	if (r == 2) goto freed ;
	else if (!r) return 0 ;
	
	sv_alltype sv_before = SV_ALLTYPE_ZERO ;
	insta = instance_check(svname) ;
	if (!insta) 
	{
		VERBO3 strerr_warnw2x("invalid instance name: ",svname) ;
		return 0 ;
	}
	if (insta > 0)
	{
		if (!instance_create(sasv,svname,SS_INSTANCE,svsrc,insta))
		{
			VERBO3 strerr_warnwu2x("create instance service: ",svname) ;
			return 0 ;
		}
		/** ensure that we have an empty line at the end of the string*/
		if (!stralloc_cats(sasv,"\n")) retstralloc(0,"parse_service_before") ;
		if (!stralloc_0(sasv)) retstralloc(0,"parse_service_before") ;
	}else if (!read_svfile(sasv,svname,svsrc)) return 0 ;
	
	memcpy(svpath,svsrc,svsrclen) ;
	memcpy(svpath + svsrclen,svname,svnamelen) ;
	svpath[svsrclen + svnamelen] = 0 ;
	
	if (sastr_cmp(parsed_list,svpath) >= 0)
	{
		VERBO2 strerr_warni2x(sv,": already added") ;
		sasv->len = 0 ;
		sv_alltype_free(&sv_before) ;
		goto freed ;
	}
		
	if (!parser(&sv_before,sasv,svname)) return 0 ;
	
	/** keep the name set by user
	 * uniquely for instantiated service
	 * The name must contain the template string */
	
	if (insta > 0 && sv_before.cname.name >= 0 )
	{
		stralloc sainsta = STRALLOC_ZERO ;
		stralloc name = STRALLOC_ZERO ;
		if (!stralloc_cats(&name,keep.s + sv_before.cname.name)) goto err ;
		
		if (!instance_splitname(&sainsta,svname,insta,0)) goto err ;
		if (sastr_find(&name,sainsta.s) == -1)
		{
			strerr_warnw2x("invalid instantiated service name: ", keep.s + sv_before.cname.name) ;
			goto err ;
		}
		stralloc_free(&sainsta) ;
		stralloc_free(&name) ;
		goto add ;
		err:
			stralloc_free(&sainsta) ;
			stralloc_free(&name) ;
			return 0 ;
	}
	else
	{
		sv_before.cname.name = keep.len ;
		if (!stralloc_catb(&keep,svname,svnamelen + 1)) return 0 ;
	}
	add:
	if (!parse_add_service(parsed_list,&sv_before,svpath,nbsv,info->owner)) return 0 ;
	
	if ((sv_before.cname.itype > CLASSIC && force > 1) || !(*exist))
		if (!parse_service_deps(info,parsed_list,&sv_before,sv,nbsv,sasv,force)) return 0 ;
	
	freed:
	return 1 ;
}