/**
 * called by cli/cron.php
 */
function cron() {

	// If called by a regular cron job it's no problem to skip it sometimes.
	if (BN=="cron.php") {
		if (!cron_lock()) return;
	} else {
		while (!cron_lock()) {
			echo "Waiting for lock ...\n";
			sleep(5);
		}
	}

	$sql_period = "SELECT *,
			debate             <= now() AS debate_now,
			preparation        <= now() AS preparation_now,
			voting             <= now() AS voting_now,
			ballot_assignment  <= now() AS ballot_assignment_now,
			ballot_preparation <= now() AS ballot_preparation_now,
			counting           <= now() AS counting_now
		FROM period";
	$result_period = DB::query($sql_period);
	while ( $period = DB::fetch_object($result_period, "Period") ) {
		/** @var Period $period */
		DB::to_bool($period->debate_now);
		DB::to_bool($period->preparation_now);
		DB::to_bool($period->voting_now);
		DB::to_bool($period->ballot_assignment_now);
		DB::to_bool($period->ballot_preparation_now);
		DB::to_bool($period->counting_now);

		$issues_start_debate = array();
		$issues_start_voting = array();
		$issues_finished_voting = array();

		// ballots
		switch ($period->state) {

			// ballot assignment
		case "ballot_application":
			if (!$period->ballot_assignment_now) break;

			$period->assign_members_to_ballots();

			$sql_ballot = "SELECT * FROM ballot WHERE period=".intval($period->id);
			$result_ballot = DB::query($sql_ballot);
			while ( $ballot = DB::fetch_object($result_ballot, "Ballot") ) {

				// notification to the ballot agents whether the ballot was approved
				// TODO: more than one ballot agent
				$notification = new Notification("ballot_approved");
				$notification->period = $period;
				$notification->ballot = $ballot;
				$sql = "SELECT member FROM offlinevoter WHERE ballot = ".intval($ballot->id)." AND agent = TRUE";
				$recipients = DB::fetchfieldarray($sql);
				$notification->send($recipients);

				if (!$ballot->approved) continue;

				// notification to the members to which ballots they were assigned
				$notification = new Notification("ballot_assigned");
				$notification->period = $period;
				$notification->ballot = $ballot;
				$sql = "SELECT member FROM offlinevoter WHERE ballot = ".intval($ballot->id);
				$recipients = DB::fetchfieldarray($sql);
				$notification->send($recipients);

			}

			$period->state = "ballot_assignment";
			$period->update(["state"]);

			break;

			// ballot preparation
		case "ballot_assignment":
			if (!$period->ballot_preparation_now) break;

			// final upload of the complete postal and ballot voters
			if ( upload_voters($period, true) ) {
				$period->state = "ballot_preparation";
				$period->update(["state"]);
			}

			// ballot_preparation is the final state.
		}


		// proposals and issues
		$sql_issue = "SELECT * FROM issue
			WHERE period=".intval($period->id)."
			AND state NOT IN ('finished', 'cancelled')";
		$result_issue = DB::query($sql_issue);
		while ( $issue = DB::fetch_object($result_issue, "Issue") ) {
			/** @var Issue $issue */

			switch ($issue->state) {

				// debate
			case "entry":
				if (!$period->debate_now) break;

				$all_proposals_revoked = true;
				$admitted_proposals = false;
				$not_admitted_proposals = false;
				foreach ( $issue->proposals() as $proposal ) {
					/** @var Proposal $proposal */
					if ($proposal->state_cancelled()) continue;
					//if ( $proposal->check_proponents() ) {
					$all_proposals_revoked = false;
					if ($proposal->state=="admitted") $admitted_proposals = true; else $not_admitted_proposals = true;
					/*} else {
						// revoke proposals without proponents
						$proposal->state = "revoked";
						$proposal->update(["state"]);
					}*/
				}

				if ($all_proposals_revoked) {
					$issue->cancel();
					break;
				}

				// None of the proposals are admitted yet.
				if (!$admitted_proposals) break;

				if ($not_admitted_proposals) {

					/* split issue
					$new_issue = new Issue;
					$new_issue->area = $issue->area;
					$new_issue->create();
					foreach ( $issue->proposals() as $proposal ) {
						if ($proposal->state_cancelled()) continue;
						if ($proposal->state=="admitted") continue;
						$proposal->issue = $new_issue->id;
						$proposal->update(["issue"]);
					}*/

					// cancel proposals
					foreach ( $issue->proposals() as $proposal ) {
						/** @var $proposal Proposal */
						if ($proposal->state_cancelled()) continue;
						if ($proposal->state=="admitted") continue;
						$proposal->cancel("cancelled_debate");
					}

				}

				$issue->state = "debate";
				$issue->update(["state"], 'debate_started=now()');

				$issues_start_debate[] = $issue;

				break;

				// preparation
			case "debate":
				if (!$period->preparation_now) break;

				// revoke proposals, which were scheduled for revokation
				revoke_before_preparation($issue);
				// don't proceed to preparation if all proposals were cancelled
				if ($issue->state == "cancelled") break;

				$issue->state = "preparation";
				$issue->update(["state"], 'preparation_started=now()');

				break;

				// voting
			case "preparation":
				if (!$period->voting_now) break;

				// collect issues for online voting start
				if ( !$issue->votingmode_offline() ) $issues_start_voting[] = $issue;
				// Issues which reached offline voting, stay in preparation state until an admin enters the voting result.

				break;

				// counting
			case "voting":
				if (!$period->counting_now) break;

				$issue->state = "counting";
				$issue->update(["state"], 'counting_started=now()');

				$issue->counting();
				$issue->finish();

				$issues_finished_voting[] = $issue;

				remove_inactive_participants($period->ngroup);

				break;
				// "finished" and "cancelled" are the final issue states.
			}

		}

		// debate start notifications
		if ($issues_start_debate) {
			$notification = new Notification("debate");
			$notification->period = $period;
			$notification->issues = $issues_start_debate;
			$notification->send();
		}

		// voting finished notifications
		if ($issues_finished_voting) {
			$notification = new Notification("finished");
			$notification->period = $period;
			$notification->issues = $issues_finished_voting;
			$notification->send();
		}

		// start voting and send individual notifications with tokens
		if ($issues_start_voting) $period->start_voting($issues_start_voting);

	}

	cron_unlock();
}
#!/usr/bin/php
<?
/**
 * upload lists of postal voters to the ID server share
 *
 * A final list of postal and ballot voters is sent by cron() when entering ballot preparation phase. This script here is an addition for uploading incomplete lists of postal voters earlier and send the letters in multiple batches. If just everything is sent in the end in one batch, it can not be guaranteed that the voters get their mailings in time. When sending in multiple batches, this risk only affects late members.
 *
 * to be called by a cron job about once per day
 *
 * crontab example:
 * 48 0  * * *  <path>/cli/cron_update_postal_voters.php
 *
 * @author Magnus Rosenbaum <*****@*****.**>
 * @package Basisentscheid
 */


if ( $dir = dirname($_SERVER['argv'][0]) ) chdir($dir);
const DOCROOT = "../";
require "../inc/common_cli.php";

// active between postage set and ballot preparation started
$sql_period = "SELECT * FROM period WHERE postage = TRUE AND state != 'ballot_preparation'";
$result_period = DB::query($sql_period);
while ( $period = DB::fetch_object($result_period, "Period") ) {
	/** @var $period Period */
	upload_voters($period);
}