<?php

/**
 * OpenKM, Open Knowledge Management System S.L.  (http://www.openkm.com)
 * Copyright (c) 2006-2018
 *
 * No bytes were intentionally harmed during the development of this application.
 *
 * This program is commercial software; you can use it under the terms of the
 * EULA - OpenKM SDK End User License Agreement as published by OpenKM Knowledge Management System S.L.
 *
 * 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
 * EULA - OpenKM SDK End User License Agreement for more details:
 * http://docs.openkm.com/kcenter/view/licenses/eula-openkm-sdk-end-user-license-agreement.html
 */

namespace openkm\impl;

use Httpful\Exception\ConnectionErrorException;
use Httpful\Mime;
use Httpful\Request;
use openkm\bean\ProcessDefinition;
use openkm\bean\ProcessInstance;
use openkm\bean\TaskInstance;
use openkm\definition\BaseWorkflow;
use openkm\exception\AccessDeniedException;
use openkm\exception\DatabaseException;
use openkm\exception\IOException;
use openkm\exception\ParseException;
use openkm\exception\RepositoryException;
use openkm\exception\UnknowException;
use openkm\exception\WorkflowException;
use openkm\util\BeanHelper;
use openkm\util\Status;
use openkm\util\TypeException;
use openkm\util\UriHelper;
use SimpleXMLElement;

/**
 * WorkflowImpl
 *
 * @author sochoa
 */
class WorkflowImpl extends ClientImpl implements BaseWorkflow {

    /**
     * Register a new workflow.
     * 
     * @param string $content Recommend using file_get_contents — Reads entire file into a string
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws IOException
     * @throws ParseException
     * @throws DatabaseException
     * @throws WorkflowException
     * @throws UnknowException
     */
    public function registerProcessDefinition($content) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_REGISTER_PROCESS_DEFINITION);
            $params = [];
            $params['content'] = $content;
            $client = Request::post($uri);
            $client->body($params, Mime::UPLOAD);
            $client->authenticateWith($this->user, $this->password);
            $response = $client->send();
            if ($response->code != Status::NO_CONTENT) {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::IO_EXCEPTION) {
                            throw new IOException($error . ': ' . $msg);
                        } else if ($error == TypeException::PARSE_EXCEPTION) {
                            throw new ParseException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Deletes a workflow.
     * 
     * @param int $pdId Is valid workflow process definition
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws DatabaseException
     * @throws WorkflowException
     * @throws UnknowException
     */
    public function deleteProcessDefinition($pdId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_DELETE_PROCESS_DEFINITION);
            $uri .= '?pdId=' . urlencode($pdId);
            $client = Request::delete($uri);
            $response = $this->getClient($client);
            if ($response->code != Status::NO_CONTENT) {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Get process definition
     * 
     * @param  int $pdId Is valid workflow process definition
     * @return ProcessDefinition Returns a workflow process definition.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function getProcessDefinition($pdId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_GET_PROCESS_DEFINITION);
            $uri .= '?pdId=' . urlencode($pdId);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                return $this->phpProcessDefinition($response->body);
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Executes a workflow on some node and returns as a result an object ProcessInstance
     * 
     * @param int $pdId Is valid workflow process definition
     * @param string $uuid The uuid or path of the document, folder, mail or record
     * @param array $formElements Are form element values needed for starting the workflow ( not all workflows need form values for starting ).
     * @return ProcessInstance Returns as a result an object ProcessInstance
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function runProcessDefinition($pdId, $uuid, $formElements = []) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_RUN_PROCESS_DEFINITION);
            $uri .= '?pdId=' . urlencode($pdId) . '&uuid=' . urlencode($uuid);
            $client = Request::put($uri);
            $formElementsComplex = [];
            foreach ($formElements as $formElement) {
                $formElementsComplex[] = BeanHelper::copyToFormElementComplex($formElement);
            }
            $formElementsComplexXML = new SimpleXMLElement('<formElementsComplex></formElementsComplex>');
            foreach ($formElementsComplex as $formElementComplex) {
                $formElementComplexXML = $formElementsComplexXML->addChild('formElementComplex');
                $formElementComplexXML->addChild('height', $formElementComplex->getHeight());
                $formElementComplexXML->addChild('label', $formElementComplex->getLabel());
                $formElementComplexXML->addChild('name', $formElementComplex->getName());
                $formElementComplexXML->addChild('objClass', $formElementComplex->getObjClass());
                $formElementComplexXML->addChild('readonly', $formElementComplex->isReadonly());
                $formElementComplexXML->addChild('type', $formElementComplex->getType());
                $formElementComplexXML->addChild('value', $formElementComplex->getValue());
                $formElementComplexXML->addChild('width', $formElementComplex->getWidth());
                foreach ($formElementComplex->getOptions() as $option) {
                    $optionXML = $formElementComplexXML->addChild('options');
                    $optionXML->addChild('label', $option->getLabel());
                    $optionXML->addChild('selected', $option->isSelected());
                    $optionXML->addChild('value', $option->getValue());
                }
                foreach ($formElementComplex->getValidators() as $validator) {
                    $validatorXML = $formElementComplexXML->addChild('validators');
                    $validatorXML->addChild('type', $validator->getType());
                    $validatorXML->addChild('parameter', $validator->getParameter());
                }
            }
            $client->body($formElementsComplexXML->asXML());
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                return $this->phpProcessInstance($response->body);
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves a list of all process instances of some registered workflows definition.
     * 
     * @param int $pdId Is valid workflow process definition
     * @return array Retrieves a list of all process instances of some registered workflows definition.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function findProcessInstances($pdId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_FIND_PROCESS_INSTANCES);
            $uri .= '?pdId=' . urlencode($pdId);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                $processInstances = [];
                foreach ($response->body->processInstance as $processInstanceXML) {
                    $processInstances[] = $this->phpProcessInstance($processInstanceXML);
                }
                return $processInstances;
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves a list of all registered workflows definitions
     * 
     * @return array(ProcessDefinition) Retrieves a list of all registered workflows definitions
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function findAllProcessDefinitions() {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_FIND_ALL_PROCESS_DEFINITIONS);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                $processDefinitions = [];
                foreach ($response->body->processDefinition as $processDefinitionXML) {
                    $processDefinitions[] = $this->phpProcessDefinition($processDefinitionXML);
                }
                return $processDefinitions;
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves a list of the last workflows definitions.
     * 
     * Can be several versions of the same workflow registered.
     * 
     * @return array(ProcessDefinition) Retrieves a list of the last workflows definitions.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function findLatestProcessDefinitions() {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_FIND_LATEST_PROCESS_DEFINITIONS);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                $processDefinitions = [];
                foreach ($response->body->processDefinition as $processDefinitionXML) {
                    $processDefinitions[] = $this->phpProcessDefinition($processDefinitionXML);
                }
                return $processDefinitions;
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves last workflow definition of some specific workflow.
     * 
     * Several workflow definition versions they have the same name.
     * 
     * @param string $name Identify an specific workflow definitions group.
     * @return ProcessDefinition Returns as a result an object ProcessDefinition
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function findLastProcessDefinition($name) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_FIND_LAST_PROCESS_DEFINITION);
            $uri .= '?name=' . urlencode($name);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                return $this->phpProcessDefinition($response->body);
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Returns the process instance.
     * 
     * @param int $piId Is a valid process instance id.
     * @return ProcessInstance Returns as a result an object ProcessInstance
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function getProcessInstance($piId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_GET_PROCESS_INSTANCE);
            $uri .= '?piId=' . urlencode($piId);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                return $this->phpProcessInstance($response->body);
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves a list of task instances assigned to the user.
     * 
     * @return array(TaskInstance) Retrieves a list of task instances assigned to the user.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function findUserTaskInstances() {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_FIND_USER_TASK_INSTANCES);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                $taskInstances = [];
                foreach ($response->body->taskInstance as $taskInstanceXML) {
                    $taskInstances[] = $this->phpTaskInstance($taskInstanceXML);
                }
                return $taskInstances;
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves a list of task instances of some process instance id.
     * 
     * @param int $piId Is a valid process instance id.
     * @return array(TaskInstance) Retrieves a list of task instances assigned to the user.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function findTaskInstances($piId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_FIND_TASK_INSTANCES);
            $uri .= "?piId=" . urlencode($piId);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                $taskInstances = [];
                foreach ($response->body->taskInstance as $taskInstanceXML) {
                    $taskInstances[] = $this->phpTaskInstance($taskInstanceXML);
                }
                return $taskInstances;
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Retrieves a list of task instances of some process instance id.
     * 
     * @param int $tiId Is a valid task instance id.
     * @param string $transName Is the chosen transaction.       
     * @param array $formElements Are form element values needed for starting the workflow ( not all workflows need form values for starting ).     
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function setTaskInstanceValues($tiId, $transName, $formElements = []) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_SET_TASK_INSTANCE_VALUES);
            $uri .= '?tiId=' . urlencode($tiId) . '&transName=' . urlencode($transName);
            $client = Request::put($uri);
            $formElementsComplex = [];
            foreach ($formElements as $formElement) {
                $formElementsComplex[] = BeanHelper::copyToFormElementComplex($formElement);
            }
            $formElementsComplexXML = new SimpleXMLElement('<formElementsComplex></formElementsComplex>');
            foreach ($formElementsComplex as $formElementComplex) {
                $formElementComplexXML = $formElementsComplexXML->addChild('formElementComplex');
                $formElementComplexXML->addChild('height', $formElementComplex->getHeight());
                $formElementComplexXML->addChild('label', $formElementComplex->getLabel());
                $formElementComplexXML->addChild('name', $formElementComplex->getName());
                $formElementComplexXML->addChild('objClass', $formElementComplex->getObjClass());
                $formElementComplexXML->addChild('readonly', $formElementComplex->isReadonly());
                $formElementComplexXML->addChild('type', $formElementComplex->getType());
                $formElementComplexXML->addChild('value', $formElementComplex->getValue());
                $formElementComplexXML->addChild('width', $formElementComplex->getWidth());
                foreach ($formElementComplex->getOptions() as $option) {
                    $optionXML = $formElementComplexXML->addChild('options');
                    $optionXML->addChild('label', $option->getLabel());
                    $optionXML->addChild('selected', $option->isSelected());
                    $optionXML->addChild('value', $option->getValue());
                }
                foreach ($formElementComplex->getValidators() as $validator) {
                    $validatorXML = $formElementComplexXML->addChild('validators');
                    $validatorXML->addChild('type', $validator->getType());
                    $validatorXML->addChild('parameter', $validator->getParameter());
                }
            }
            $client->body($formElementsComplexXML->asXML());
            $response = $this->getClient($client);
            if ($response->code != Status::NO_CONTENT) {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Returns a task instance.
     * 
     * @param int $tiId Is a valid task instance id.
     * @return TaskInstance Returns as a result an object TaskInstance
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function getTaskInstance($tiId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_GET_TASK_INSTANCE);
            $uri .= "?tiId=" . urlencode($tiId);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                return $this->phpTaskInstance($response->body);
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * 
     * Starts a task instance.
     * 
     * @param int $tiId Is a valid task instance id.
     * @param string $actorId Must be some valid OpenKM userId.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function setTaskInstanceActorId($tiId, $actorId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_SET_TASK_INSTANCE_ACTOR_ID);
            $uri .= '?tiId=' . urlencode($tiId) . '&actorId=' . urlencode($actorId);
            $client = Request::put($uri);
            $response = $this->getClient($client);
            if ($response->code != Status::NO_CONTENT) {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Starts a task instance.
     * 
     * @param int $tiId Is a valid task instance id.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function startTaskInstance($tiId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_START_TASKINSTANCE);
            $uri .= '?tiId=' . urlencode($tiId);
            $client = Request::put($uri);
            $response = $this->getClient($client);
            if ($response->code != Status::NO_CONTENT) {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Ends a task instance.
     * 
     * @param int $tiId Is a valid task instance id.
     * @param string $transName Is the chosen transaction.       
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws RepositoryException
     * @throws WorkflowException
     * @throws DatabaseException
     * @throws UnknowException
     */
    public function endTaskInstance($tiId, $transName) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_END_TASKINSTANCE);
            $uri .= '?tiId=' . urlencode($tiId) . '&transName=' . urlencode($transName);
            $client = Request::put($uri);
            $response = $this->getClient($client);
            if ($response->code != Status::NO_CONTENT) {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

    /**
     * Return a map with all the process definition forms.
     * 
     * @param int $pdId Is valid workflow process definition
     * @return array[String,array[formElement]] Return a map with all the process definition forms.
     * @throws ConnectionErrorException
     * @throws AccessDeniedException
     * @throws ParseException
     * @throws RepositoryException
     * @throws DatabaseException
     * @throws WorkflowException
     * @throws UnknowException
     */
    public function getProcessDefinitionForms($pdId) {
        try {
            $uri = UriHelper::getUri($this->host, UriHelper::WORKFLOW_GET_PROCESS_DEFINITION_FORMS);
            $uri .= '?pdId=' . urlencode($pdId);
            $client = Request::get($uri);
            $response = $this->getClient($client);
            if ($response->code == Status::OK) {
                $processDefinitionForms = [];
                foreach ($response->body->processDefinitionForm as $processDefinitionFormXML) {
                    $formElements = [];
                    foreach ($processDefinitionFormXML->formElementsComplex as $formElementComplexXML) {
                        $formElements[] = BeanHelper::copyToFormElement($this->phpFormElementComplex($formElementComplexXML));
                    }
                    $processDefinitionForms[(string) $processDefinitionFormXML->key] = $formElements;
                }
                return $processDefinitionForms;
            } else {
                if ($response->code == Status::UNAUTHORIZED) {
                    throw new AccessDeniedException($response->body, $response->code);
                } else if ($response->code == Status::INTERNAL_SERVER_ERROR) {
                    $position = strpos($response->body, ':');
                    if ($position) {
                        $error = substr($response->body, 0, $position);
                        $msg = substr($response->body, $position + 1, strlen($response->body));
                        if ($error == TypeException::PARSE_EXCEPTION) {
                            throw new ParseException($error . ': ' . $msg);
                        } else if ($error == TypeException::REPOSITORY_EXCEPTION) {
                            throw new RepositoryException($error . ': ' . $msg);
                        } else if ($error == TypeException::DATABASE_EXCEPTION) {
                            throw new DatabaseException($error . ': ' . $msg);
                        } else if ($error == TypeException::WORKFLOW_EXCEPTION) {
                            throw new WorkflowException($error . ': ' . $msg);
                        } else {
                            throw new UnknowException($error . ': ' . $msg);
                        }
                    } else {
                        throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                    }
                } else {
                    throw new UnknowException("HTTP error code " . $response->code . ": " . $response->body);
                }
            }
        } catch (ConnectionErrorException $cee) {
            throw new ConnectionErrorException('ConnectionErrorException: ' . $cee->getMessage());
        }
    }

}

?>
