./ MultiCS.r82 / loadbalance.c
///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

// 0: different ; 1:~equivalent
int cs_cmp_card( struct cs_card_data *card, struct cardserver_data *cs)
{
	int i,j,found;
	int nbsame = 0;
	int nbdiff = 0;

	if (card->caid!=cs->card.caid) return 0;

/*
	if ( ((card->caid & 0xff00)==0x1800)
		|| ((card->caid & 0xff00)==0x0900)
		|| ((card->caid & 0xff00)==0x0b00) ) return 1;
*/
	if ( ((card->caid & 0xff00)!=0x0100) && ((card->caid & 0xff00)!=0x0500) ) return 1;

	for(i=0; i<card->nbprov;i++) {
		found = 0;
		for(j=0; j<cs->card.nbprov;j++)
			if (card->prov[i]==cs->card.prov[j].id) {
				found = 1;
				break;
			}
		if (found) nbsame++; else nbdiff++;
	}

	if ( (nbsame==card->nbprov)||(nbsame==cs->card.nbprov) ) return 1;
	return 0;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

int match_card( uint16_t caid, uint32_t prov, struct cs_card_data* card)
{
	if (caid!=card->caid) return 0;
	// Dont care about provider for caid non via/seca
	if ( ((card->caid & 0xff00)!=0x0100) && ((card->caid & 0xff00)!=0x0500) ) return 1;
	int i;
	for(i=0; i<card->nbprov;i++) if (prov==card->prov[i]) return 1;
	return 0;
}

// Search for a card with best sid val.
// TODO: option validecmtime-ecmtime
int sidata_getval(struct server_data *srv, struct cardserver_data *cs, uint16_t caid, uint32_t prov, uint16_t sid, struct cs_card_data **selcard )
{
	struct cs_card_data *card = NULL;

	*selcard = NULL;
	if ( (srv->type==TYPE_NEWCAMD) || (srv->type==TYPE_RADEGAST) || (srv->type==TYPE_CAMD35) || (srv->type==TYPE_CS378X) ) {
		card = srv->card;
		while (card) {
			if ( match_card(caid,prov,card) ) break;
			card = card->next;
		}
		*selcard = card;
		if (card) {
			struct sid_data *sidata = card->sids[sid>>8];
			while (sidata) {
				if (sidata->sid==sid)
				if (sidata->prov==prov) return sidata->val;
				sidata = sidata->next;			
			}
		}
		return 0; // Channel not found in sid cache
	}
#ifdef CCCAM_CLI
	else if (srv->type==TYPE_CCCAM) {
		// Search for availabe card cannot be found in sids
		*selcard = NULL;
		int selsidvalue = 0;
		card = srv->card;
		while (card) {
			if ( match_card(caid,prov,card)
				|| ( !prov && cs && cs_cmp_card(card, cs) ) // use for prov=0
			) {
				// Search fo sid
				int sidvalue = 0; // by default

				struct sid_data *sidata = card->sids[sid>>8];
				while (sidata) {
					if ( (sidata->sid==sid)&&(sidata->prov==prov) ) {
						sidvalue = sidata->val;
						break;
					}
					sidata = sidata->next;			
				}

				// Check with Selected card (sidvalue) TODO: classify by ecmtime, decode, stability
				if (*selcard) {
					if ( selsidvalue<0 ) {
						if (sidvalue>=0) { *selcard = card; selsidvalue = sidvalue; }
						else if (card->uphops<(*selcard)->uphops) { *selcard = card; selsidvalue = sidvalue; }
					}
					else if ( selsidvalue==0 ) {
						if (sidvalue>0) { *selcard = card; selsidvalue = sidvalue; }
						else if (sidvalue==0) if (card->uphops<(*selcard)->uphops) { *selcard = card; selsidvalue = sidvalue; }
					}
					else if (sidvalue>0) if (card->uphops<(*selcard)->uphops) { *selcard = card; selsidvalue = sidvalue; }
				}
				else { *selcard = card; selsidvalue = sidvalue; }
			}
			card = card->next;
		}
		return selsidvalue;
	}
#endif
	return 0;
}



#define MAXSRVTAB 255

struct srvtab_data
{
	struct server_data *srv;
	struct cs_card_data *card; // selected card
	uint32_t shareid; // Selected Card ShareID
	int uphops;
	int val; // sid value
	unsigned int ecmtime; // Card Ecmtime
	uint32_t ecmperhr;
};


struct srvtab_data srvlist[MAXSRVTAB];
struct srvtab_data *psrvlist[MAXSRVTAB];
struct srvtab_data *srvtemp;


int srvtab_arrange(struct cardserver_data *cs, ECM_DATA *ecm, int bestone )
{
	int i,j;
	int nbsrv = 0;
	struct server_data *srv;

	memset( srvlist, 0 , sizeof(srvlist) );
	nbsrv = 0;

	// MULTICARD Servers Selection (Newcamd,CCcam,Mgcamd...) ;)
	unsigned int ticks = GetTickCount();
	srv = cfg.server;
	while ( srv && (nbsrv<MAXSRVTAB) ) {
		if ( !IS_DISABLED(srv->flags)&&(srv->connection.status>0) )
		if (
			( cs->option.fallownewcamd && (srv->type==TYPE_NEWCAMD) )
			|| ( cs->option.fallowcccam && (srv->type==TYPE_CCCAM) )
			|| ( cs->option.fallowradegast && (srv->type==TYPE_RADEGAST) )
			|| ( cs->option.fallowcamd35 && (srv->type==TYPE_CAMD35) )
			|| ( cs->option.fallowcs378x && (srv->type==TYPE_CS378X) )
		)
		// Remove Circular request: check for client ip & srv ip
		if ( (srv->host->ip==0x0100007F) || ( !ecm_checkip(ecm, srv->host->ip) && !ecm_checksrvip(ecm, srv->host->ip) ) )
		{
			// Check for CS PORTS
			for(i=0; i<MAX_CSPORTS; i++ ) {
				if (!srv->csport[i]) break;
				if (srv->csport[i]==cs->newcamd.port) {
					i=0;
					break;
				}
			}
			if (i==0) { // ADD TO PROFILE
				//Check for used servers, dont reuse
				for (i=0; i<20;i++) {
					if (!ecm->server[i].srvid) break;
					if (ecm->server[i].srvid==srv->id) break;
				}
				if ( (i>=20)||(ecm->server[i].srvid!=srv->id) ) {
					// Check for ECM TIMEOUT
					if ( (srv->busy)&&((srv->lastecmtime+9000)<ticks) ) { // timeout
						debugf(getdbgflag(DBG_SERVER,0,srv->id)," ??? server (%s:%d) doesnt send ecm answer\n", srv->host->name,srv->port);
						srv->ecmtimeout++;
						srv->busy = 0;
						disconnect_srv(srv);
					}
					else {
						// Check for newcamd server sids
						i = 0;
						if ( srv->sids && (srv->type==TYPE_NEWCAMD) ) {
							struct sid_chid_data *sid = srv->sids;
							for(i=0; i<MAX_SIDS; i++,sid++) {
								if ( sid->sid==0 ) break;
								if (sid->sid==ecm->sid)
								if (!sid->chid || (sid->chid==ecm->chid) ) { i=0; break; }
							}
						}
						if (i==0) {
							// check for any card to decode
							pthread_mutex_lock(&srv->lock);

							// best card to decode is selected, it may there is only worst one but is returned
							struct cs_card_data *pcard = NULL;
							int val = sidata_getval( srv, cs, ecm->caid, ecm->provid, ecm->sid, &pcard);
							if ( !cs->option.maxfailedecm || (val > -cs->option.maxfailedecm) ) {  // available card+sid : block card that have decode failed on sid
								if (pcard) {
									int ecmtime = 0;
									if (srv->type==TYPE_CCCAM)
										if (pcard->ecmok>10) ecmtime = pcard->ecmoktime/pcard->ecmok; else ecmtime = 0;
									else
										if (srv->ecmok>10) ecmtime = srv->ecmoktime/srv->ecmok; else ecmtime = 0;
									if ( !cs->option.server.validecmtime || (ecmtime<cs->option.server.validecmtime) ) {
										srvlist[nbsrv].srv = srv;
										srvlist[nbsrv].card = pcard; // default card
										srvlist[nbsrv].shareid = pcard->shareid; // default card
										srvlist[nbsrv].uphops = pcard->uphops;
										srvlist[nbsrv].val = val;
										srvlist[nbsrv].ecmtime = ecmtime;
										psrvlist[nbsrv] = &srvlist[nbsrv];
										nbsrv++;
									}
								}
							}

							pthread_mutex_unlock(&srv->lock);
						}
					}
				}
			}
		}
		srv = srv->next;
	}
	//debugf(0, " A*srvtab_arrange(%04x:%06x:%04x) Servers = %d\n", ecm->caid, ecm->provid, ecm->sid, nbsrv);

	//Remove Cardservers with delay time
	if (cs->option.server.timeperecm) {
		i=0;
		for(j=0; j<nbsrv; j++) {
			//if ( (psrvlist[j]->srv->host->ip!=0x0100007F)&&(psrvlist[j]->srv->type==TYPE_NEWCAMD) )
			if ( (psrvlist[j]->srv->type==TYPE_NEWCAMD) ) {
				unsigned int msperecm = ( (ticks-psrvlist[j]->srv->connection.time) + psrvlist[j]->srv->connection.uptime ) / (psrvlist[j]->srv->ecmnb+1);
				unsigned int tim;
				if ( msperecm > (2*cs->option.server.timeperecm) ) tim = 0;
				else if ( msperecm > cs->option.server.timeperecm ) tim = (2*cs->option.server.timeperecm)-msperecm;
				else tim = cs->option.server.timeperecm;
				if ( (psrvlist[j]->srv->lastecmtime+tim)<=ticks ) {
					if (i<j) psrvlist[i] = psrvlist[j];
					i++;
				}
			}
			else {
				if (i<j) psrvlist[i] = psrvlist[j];
				i++;
			}
		}
		psrvlist[i] = NULL;
		nbsrv = i;
	}
	//debugf(0, " B*srvtab_arrange(%04x:%06x:%04x) Servers = %d\n", ecm->caid, ecm->provid, ecm->sid, nbsrv);

	// Store number of available servers, Runtime ADD SIDS
	if (ecm->sid) {
		for(i=0; i<1024; i++) {
			if (cs->deniedsids[i].sid==ecm->sid) {
				cs->deniedsids[i].nbsrv = nbsrv;
				break;
			}
			if (!cs->deniedsids[i].sid) {
				cs->deniedsids[i].sid = ecm->sid;
				cs->deniedsids[i].nbsrv = nbsrv;
				break;
			}
		}
	}


	// Check if there is no/few servers to decode, send decode failed to client
	// dont get from few servers (for many cccam servers)
	if (nbsrv<=cs->option.server.threshold) {
		return -1;
	}


	// Remove Busy Servers
	i=0;
	for(j=0; j<nbsrv; j++) {
		if (!psrvlist[j]->srv->busy) {
			if (i<j) psrvlist[i] = psrvlist[j];
			i++;
		}
	}
	psrvlist[i] = NULL;
	nbsrv = i;


//// ARRANGE

	// Arrange by ECM LAST SENT TIME
	for(i=0; i<nbsrv-1; i++)
		for(j=i+1; j<nbsrv; j++)
			if ( psrvlist[i]->srv->lastecmtime > psrvlist[j]->srv->lastecmtime ) {
				srvtemp = psrvlist[i];
				psrvlist[i] = psrvlist[j];
				psrvlist[j] = srvtemp;
			}

	ticks=GetTickCount();
	for(i=0; i<nbsrv; i++)
		psrvlist[i]->ecmperhr = (psrvlist[i]->srv->ecmnb*3600*1000) / ( 1+ (ticks-psrvlist[i]->srv->connection.time)+psrvlist[i]->srv->connection.uptime );

	if (!bestone)

		// Arrange by ECM LAST SENT TIME && unbusy state & sid ok
		for(i=0; i<nbsrv-1; i++)
		for(j=i+1; j<nbsrv; j++) {

				if ( (psrvlist[i]->val>=0)&&(psrvlist[i]->srv->priority > psrvlist[j]->srv->priority) ) continue;
				if ( (psrvlist[j]->val>=0)&&(psrvlist[j]->srv->priority > psrvlist[i]->srv->priority) ) { // check if using local card
					srvtemp = psrvlist[i];
					psrvlist[i] = psrvlist[j];
					psrvlist[j] = srvtemp;
				}
				else if (psrvlist[i]->val>=0) {
					if (psrvlist[j]->val>=0) {
						// Check for card uphops
						if (psrvlist[i]->uphops > psrvlist[j]->uphops) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
						else if  ( psrvlist[i]->ecmperhr > psrvlist[j]->ecmperhr ) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
					}
				}
				else if (psrvlist[i]->val==-1) {
					if (psrvlist[j]->val>=0) {
						srvtemp = psrvlist[i];
						psrvlist[i] = psrvlist[j];
						psrvlist[j] = srvtemp;
					}
					else if (psrvlist[j]->val==-1) {
						if  ( psrvlist[i]->ecmperhr > psrvlist[j]->ecmperhr ) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
					}
				}
				else {
					if (psrvlist[j]->val>=-1) {
						srvtemp = psrvlist[i];
						psrvlist[i] = psrvlist[j];
						psrvlist[j] = srvtemp;
					}
					else {
						if  ( psrvlist[i]->ecmperhr > psrvlist[j]->ecmperhr ) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
					}
				}
		}

	else

		for(i=0; i<nbsrv-1; i++)
		for(j=i+1; j<nbsrv; j++) {

				if ( (psrvlist[i]->val>0)&&(psrvlist[i]->srv->priority > psrvlist[j]->srv->priority) ) continue;

				if (psrvlist[i]->val>0) {
					if ( psrvlist[j]->srv->priority > psrvlist[i]->srv->priority ) {
						srvtemp = psrvlist[i];
						psrvlist[i] = psrvlist[j];
						psrvlist[j] = srvtemp;
					}
					else if (psrvlist[j]->val>0) {
						if  ( ( psrvlist[i]->ecmperhr > psrvlist[j]->ecmperhr ) ) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
					}
				}
				else if (psrvlist[i]->val==0) {
					if (psrvlist[j]->val>0) {
						srvtemp = psrvlist[i];
						psrvlist[i] = psrvlist[j];
						psrvlist[j] = srvtemp;
					}
					else if (psrvlist[j]->val==0) {
						if  ( ( psrvlist[i]->ecmperhr > psrvlist[j]->ecmperhr ) ) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
					}
				}
				else {
					if (psrvlist[j]->val>=0) {
						srvtemp = psrvlist[i];
						psrvlist[i] = psrvlist[j];
						psrvlist[j] = srvtemp;
					}
					else {
						if  ( psrvlist[i]->val < psrvlist[j]->val ) {
							srvtemp = psrvlist[i];
							psrvlist[i] = psrvlist[j];
							psrvlist[j] = srvtemp;
						}
					}
				}
		}

	return nbsrv;

}