package es.uv.saic.web;

import java.io.IOException;
import java.text.CharacterIterator;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.text.StringCharacterIterator;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.xml.parsers.ParserConfigurationException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import es.uv.saic.domain.Informe;
import es.uv.saic.domain.InformeProcessos;
import es.uv.saic.domain.Instancia;
import es.uv.saic.domain.Link;
import es.uv.saic.domain.Categoria;
import es.uv.saic.domain.Document;
import es.uv.saic.domain.Grafica;
import es.uv.saic.domain.Indicador;
import es.uv.saic.domain.Organ;
import es.uv.saic.domain.Usuari;
import es.uv.saic.domain.UsuarisRol;
import es.uv.saic.dto.AnyDimensioDTO;
import es.uv.saic.dto.ArchiveOrganDTO;
import es.uv.saic.dto.CategoriaDTO;
import es.uv.saic.dto.CategoriaDocumentDTO;
import es.uv.saic.dto.DimensioInstanciesDTO;
import es.uv.saic.dto.DocumentDTO;
import es.uv.saic.dto.DocumentTmpDTO;
import es.uv.saic.dto.IndicadorDTO;
import es.uv.saic.dto.InstanciaDTO;
import es.uv.saic.dto.InstanciaGanttDTO;
import es.uv.saic.dto.TreeDTOAny;
import es.uv.saic.dto.TreeDTODimensio;
import es.uv.saic.dto.TreeDTOInstancia;
import es.uv.saic.dto.TreeDTOOrgan;
import es.uv.saic.feign.DocumentClient;
import es.uv.saic.service.CategoriaService;
import es.uv.saic.service.GraficaService;
import es.uv.saic.service.IndicadorEnquestaService;
import es.uv.saic.service.IndicadorService;
import es.uv.saic.service.InformeService;
import es.uv.saic.service.InstanciaService;
import es.uv.saic.service.LinkService;
import es.uv.saic.service.OrganService;
import es.uv.saic.service.UsuarisRolService;

@Controller
public class DashboardController {

	@Autowired 
	private OrganService os;
	@Autowired
	private UsuarisRolService urs;
	@Autowired
	private InformeService infs;
	@Autowired
	private InstanciaService is;
	@Autowired
	private CategoriaService cs;
	@Autowired
	private IndicadorService ids;
	@Autowired
	private IndicadorEnquestaService ies;
	@Autowired
	private GraficaService gs;
	@Autowired
	private LinkService ls;

	@Value("${saic.data.filePath}")
	private String filePath;

	@Autowired
	private DocumentClient dc;

	@Value("${saic.url.public}")
	private String publicUrl;

	@Value("${saic.data.templates.fileNotFound}")
	private String fileNotFound;
	
	// GET para cargar el dashboard con toda su información
	@GetMapping("/dashboard")
	public String getOrganList(Model model, Authentication auth) {
		List<Organ> titulacions = new ArrayList<Organ>();
		List<Organ> centres = new ArrayList<Organ>();
		boolean showCentres = false;
		Usuari u = (Usuari) auth.getPrincipal();
		
		if(urs.isGrantedUser(u)){
			Organ o = os.findByID("C", 0);
			return "redirect:/dashboard/"+o.getRuct();
		}		
		else if(urs.isGrantedSupervisor(u)) {
			titulacions = os.getTitulacions().stream().filter(o -> o.getOrgan().getId().getLugar() != 99).collect(Collectors.toList());
		}
		else {
			List<UsuarisRol> rols = this.urs.findActiveRols(u);
			for(UsuarisRol rol : rols) {
				if(rol.getOrgan().getId().getTlugar().equals("C")) {
					titulacions.addAll(this.os.findTitulacionsByCentre(Arrays.asList(rol.getOrgan().getId().getLugar())));
					centres.add(rol.getOrgan());
					showCentres = true;
				}
				else if(rol.getOrgan().getId().getTlugar().equals("T")) {
					titulacions.add(rol.getOrgan());
				}
			}
			
			titulacions.sort(Comparator.comparing(a -> a.getNomCas()));
			titulacions = titulacions.stream()
									 .distinct()
									 .collect(Collectors.toList());
			centres = centres.stream()
							 .distinct()
							 .collect(Collectors.toList());
		}
		if(centres.size() == 1) {
			return "redirect:/dashboard/"+centres.get(0).getRuct();
		}
		else if(titulacions.size() == 1) {
			return "redirect:/dashboard/"+titulacions.get(0).getRuct();
		}
		else {
			model.addAttribute("showCentres", showCentres);
			model.addAttribute("titulacions", titulacions);
			return "dashboard";
		}
	}
	
	//GET para mostrar el dashboard a partir de un numero ruct
	@GetMapping("/dashboard/{ruct}")
	public String getDashboardOrgan(Model model, Authentication auth, @PathVariable("ruct") Integer ruct) {
		Organ o = os.findByRuct(ruct);
		o.setCodis();
		Usuari u = (Usuari) auth.getPrincipal();
		if(!this.isSuitable(o, u)) {
			return "401";
		}
		
		if(o.getTambit().equals("C") && o.getId().getTlugar().equals("C") && o.getId().getLugar() == 0) {
			List<Organ> tits = os.getTitulacions();
			tits.forEach( (t) -> t.setCodis() );
			model.addAttribute("titulacions", tits);
			this.loadManagers(model, o.getId().getLugar(), o.getId().getLugar());
		}
		else if(o.getTambit().equals("C")){
			List<Organ> tits = this.os.getTitulacionsByCentre(o.getId().getLugar());
			model.addAttribute("titulacions", tits);
			this.loadManagers(model, o.getId().getLugar(), o.getId().getLugar());	
		}
		else {
			this.loadManagers(model, o.getOrgan().getId().getLugar(), o.getId().getLugar());
		}
		List<Categoria> c = cs.findFirstLevel(o.getId().getTlugar());
		model.addAttribute("organ", o);
		model.addAttribute("categories", c);		
		model.addAttribute("editable", ((Usuari)auth.getPrincipal()).isAdmin() || ((Usuari)auth.getPrincipal()).isGranted());
		model.addAttribute("showCentres", false);
		if(o.getTambit().equals("C") && o.getId().getTlugar().equals("C") && o.getId().getLugar() == 0) {
			model.addAttribute("showCentres", true);
			c = cs.findFirstLevelAndU(o.getId().getTlugar());
			model.addAttribute("categories", c);	
			return "dashboardUniversitat";
		}
		else if(o.getTambit().equals("C")) {
			return "dashboardCentre";
		}
		return "dashboardTitulacio";
	}
	
	// GET para conseguir todos los procedimiento a partir del idTitulacio
	@GetMapping("/dashboard/procedures/{idTitulacio}")
	@ResponseBody
	public List<?> loadReports(Model model, Authentication auth, @PathVariable("idTitulacio") Integer idTitulacio) {
		String locale = LocaleContextHolder.getLocale().getLanguage();
		Organ o = os.findByRuct(idTitulacio);
		List<Informe> informes = this.infs.findByGrupWebTambit("D", o.getTambit());
		List<AnyDimensioDTO> treeByTitulacio = new ArrayList<AnyDimensioDTO>();
		List<TreeDTOAny> treeByCentre = new ArrayList<TreeDTOAny>();
		int currentYear = Year.now().getValue();
		for(int i=currentYear; i>=currentYear-5; i--) {
			
			AnyDimensioDTO treeAnyT = new AnyDimensioDTO();
			TreeDTOAny treeAnyC = new TreeDTOAny();
			if(o.getTambit().equals("C")){
				treeAnyC.setText(Integer.toString(i-1)+" - "+Integer.toString(i));
				treeAnyC.setChildren(new ArrayList<TreeDTODimensio>());
			}
			else{
				treeAnyT.setText(Integer.toString(i-1)+" - "+Integer.toString(i));
				treeAnyT.setChildren(new ArrayList<DimensioInstanciesDTO>());
			}
			
			for(Informe dim : informes) {
				List<String> processos = new ArrayList<String>();
				for(InformeProcessos ip : dim.getProcessos()) {
					processos.add(ip.getNomProces());
				}
				// Create Dimensio
				List<TreeDTOOrgan> treeOrgans = new ArrayList<TreeDTOOrgan>();
				
				List<Instancia> instanciesT = new ArrayList<Instancia>();
				List<Instancia> instanciesC = new ArrayList<Instancia>();
				List<Instancia> instanciesC0 = new ArrayList<Instancia>();
				if(o.getTambit().equals("C")) {
					instanciesC = is.findByOrganCursNom(o.getId().getTlugar(), o.getId().getLugar(), 
														o.getId().getLugar(), 1, i, processos);
					instanciesC.addAll(is.findByOrganCursNom(o.getId().getTlugar(), o.getId().getLugar(), 
															 o.getId().getLugar(), 2, i, processos));
					instanciesC.addAll(is.findByOrganCursNom(o.getId().getTlugar(), o.getId().getLugar(), 
															 o.getId().getLugar(), 3, i, processos));
					instanciesC0 = is.findByOrganCursNom(o.getId().getTlugar(), o.getId().getLugar(), 
														 o.getId().getLugar(), 0, i, processos);

					instanciesC.addAll(instanciesC0);

					TreeDTOOrgan treeOrgC = new TreeDTOOrgan();
					treeOrgC.setText("Centro");
					treeOrgC.setChildren(instanciesC.stream().map(x -> new TreeDTOInstancia(x)).collect(Collectors.toList()));

					treeOrgans.add(treeOrgC);

					List<Organ> orgChilds = os.findTitulacionsByCentre(Stream.of(o.getId().getLugar()).toList());
					Collections.sort(orgChilds, new Comparator<Organ>(){
						public int compare(Organ s1, Organ s2) {
							return s1.getNomCas().compareToIgnoreCase(s2.getNomCas());
						}
					});

					for(Organ tit : orgChilds){
						instanciesT = is.findByOrganCursNom(tit.getId().getTlugar(), tit.getId().getLugar(), 
														    tit.getOrgan().getId().getLugar(), 
														    tit.getId().getLugar(), i, processos);

						TreeDTOOrgan treeOrg = new TreeDTOOrgan();
						treeOrg.setText(tit.getNomCas());
						treeOrg.setChildren(instanciesT.stream().map(x -> new TreeDTOInstancia(x)).collect(Collectors.toList()));

						treeOrgans.add(treeOrg);
					}

				}
				else {
					instanciesT = is.findByOrganCursNom(o.getId().getTlugar(), o.getId().getLugar(), 
														o.getOrgan().getId().getLugar(), 
														o.getId().getLugar(), i, processos);
					Integer g = 1;
					if(o.getTambit().equals("M")) {
						g = 2;
					}
					else if(o.getTambit().equals("D")) {
						g = 3;
					}
					instanciesC = is.findByOrganCursNom(o.getOrgan().getId().getTlugar(), 
														o.getOrgan().getId().getLugar(), 
														o.getOrgan().getId().getLugar(), 
														g, i, processos);
					instanciesC0 = is.findByOrganCursNom(o.getOrgan().getId().getTlugar(), 
														 o.getOrgan().getId().getLugar(), 
														 o.getOrgan().getId().getLugar(), 
														 0, i, processos);
				}

				if(o.getTambit().equals("C")){
					TreeDTODimensio treeDim = new TreeDTODimensio();
					treeDim.setText("["+dim.getNom()+"]  "+(locale.equals("es") ? dim.getTitolCas() : dim.getTitolVal()));
					treeDim.setChildren(treeOrgans);
					treeAnyC.getChildren().add(treeDim);
				}
				else {
					instanciesT.addAll(instanciesC);
					instanciesT.addAll(instanciesC0);
					List<InstanciaDTO> ints = instanciesT.stream().map(x -> new InstanciaDTO(x)).collect(Collectors.toList());
					DimensioInstanciesDTO dimension = new DimensioInstanciesDTO();
					dimension.setText("["+dim.getNom()+"]  "+(locale.equals("es") ? dim.getTitolCas() : dim.getTitolVal()));
					dimension.setChildren(ints);
					treeAnyT.getChildren().add(dimension);
				}

				
			}
			if(o.getTambit().equals("C")){
				treeByCentre.add(treeAnyC);
			}
			else {
				treeByTitulacio.add(treeAnyT);
			}
			
		}
		if(o.getTambit().equals("C")){
			return treeByCentre;
		}
		else {
			return treeByTitulacio;
		}
	}
	
	// GET para conseguir la documentación ya a aportada a partir del id de la tituación
	@GetMapping("/dashboard/documents/{idTitulacio}")
	@ResponseBody
	public List<CategoriaDocumentDTO> loadDocuments(Model model, @PathVariable("idTitulacio") Integer idTitulacio){
		String locale = LocaleContextHolder.getLocale().getLanguage();
		Organ o = os.findByRuct(idTitulacio);
		List<Categoria> parents = cs.findFirstLevelAndU(o.getId().getTlugar());
		List<CategoriaDocumentDTO> data = new ArrayList<CategoriaDocumentDTO>();
		for(Categoria c : parents) {
			CategoriaDocumentDTO cat = new CategoriaDocumentDTO();
			cat.setText(locale.equals("es") ? c.getNomCas() : c.getNomVal());
			cat.setChildren(new ArrayList<DocumentDTO>());
			
			List<Categoria> catChilds = cs.findByPareTambitAndU(c.getIdCategoria(), o.getTambit());
			for(Categoria cc : catChilds) {
				Document doc = findByCategoriaOrgan(cc.getIdCategoria(), o.getId().getLugar(), o.getId().getTlugar());
				if(doc != null) {
					DocumentDTO docdto = new DocumentDTO(locale.equals("es") ? cc.getNomCas() : cc.getNomVal(), 
														 doc.getIdDocument().toString(), 
														 String.format("%td-%<tm-%<tY", doc.getData()),
														 this.getSize(doc.getRuta()));
					cat.getChildren().add(docdto);
				}
			}
			if(cat.getChildren().size() == 0) {
				DocumentDTO docdto = new DocumentDTO();
				docdto.setText(locale.equals("es") ? "No hay documentos subidos en esta categoría" : "No hi ha documents pujats en aquesta categoria");
				docdto.setRuta("");
				cat.getChildren().add(docdto);
			}
			data.add(cat);
		}
				
		return data;
	}

	// GET para conseguir el diagrama de gantt a partir del ruct
	@GetMapping("/dashboard/gantt/{ruct}")
	@ResponseBody
	public List<InstanciaGanttDTO> loadGantt(Model model, @PathVariable("ruct") Integer ruct){
		Organ o = os.findByRuct(ruct);
		List<Integer> ambits = Arrays.asList(1, 2, 3, 0);
		if(!o.getTambit().equals("C")) {
			if(o.getTambit().equals("G")) {
				ambits = Arrays.asList(1, 0);
			}
			else if(o.getTambit().equals("M")) {
				ambits = Arrays.asList(2, 0);
			}
			else {
				ambits = Arrays.asList(3, 0);
			}
		}
		
		int currentYear = Year.now().getValue();
		List<InstanciaGanttDTO> data = new ArrayList<InstanciaGanttDTO>();
		List<InstanciaGanttDTO> instancies = new ArrayList<InstanciaGanttDTO>();
		
		if(o.getTambit().equals("C")) {
			instancies = this.is.findByOrganBetweenCurs("C", o.getId().getLugar(), o.getId().getLugar(), ambits, currentYear-5, currentYear);
		}
		else {
			instancies = this.is.findByOrganBetweenCurs("T", o.getId().getLugar(), o.getOrgan().getId().getLugar(), Arrays.asList(o.getId().getLugar()), currentYear-5, currentYear);
			data.addAll(instancies);
			instancies = this.is.findByOrganBetweenCurs("C", o.getOrgan().getId().getLugar(), o.getOrgan().getId().getLugar(), ambits, currentYear-5, currentYear);
		}
		data.addAll(instancies);
		
		Collections.sort(data, new Comparator<InstanciaGanttDTO>() {  
			@Override  
			public int compare(InstanciaGanttDTO a, InstanciaGanttDTO b) {  
				DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
				LocalDateTime datea = LocalDateTime.parse(a.getStart()+" 00:01", f);
				LocalDateTime dateb = LocalDateTime.parse(b.getStart()+" 00:01", f);
				boolean isBefore = datea.isBefore(dateb);
				return isBefore ? -1 : 1;
			}  
		});
						
		return data;
	}
		
	@GetMapping("/dashboard/documents/cats/{idCategoria}/{tambit}")
	@ResponseBody
	public List<Categoria> getDocumentChildCats(@PathVariable("idCategoria") Integer idCategoria, @PathVariable("tambit") String tambit){
		List<Categoria> c = cs.findByPareTambit(idCategoria, tambit);
		return c;
	}

	@GetMapping("/dashboard/documents/catsu/{idCategoria}/{tambit}")
	@ResponseBody
	public List<Categoria> getDocumentChildCatsU(@PathVariable("idCategoria") Integer idCategoria, @PathVariable("tambit") String tambit){
		List<Categoria> c = cs.findByPareTambitAndU(idCategoria, tambit);
		System.out.println(c);
		return c;
	}
	
	// POST para guardar un documento en el sistema
	@PostMapping("/dashboard/documents")
	@ResponseBody
	public void uploadDocument(Model model, @RequestParam MultipartFile file, @RequestParam("idCategoria") Integer idCategoria, 
							   @RequestParam("lugar") Integer lugar, @RequestParam("tlugar") String tlugar) throws IllegalStateException, IOException {
		
		Categoria cat = this.cs.findById(idCategoria);
		Organ org = this.os.findByID(tlugar, lugar);
		Document doc = findByCategoriaOrgan(idCategoria, lugar, tlugar);
		if(doc != null) {
			this.archive(doc);
		}
		doc = new Document();
		doc.setCategoria(cat);
		doc.setOrgan(org);
		doc.setData(new Date(System.currentTimeMillis()));
		doc.setNom(cat.getNomCas());
		doc.setVisible(true);
		doc = save(doc);
		String path = upload(doc.getIdDocument(), cat.getIdCategoria(), lugar, tlugar, file);
		doc.setRuta(path);
		save(doc);
	}

	// POST para añadir un documento a un centro cocncreto
	@PostMapping("/dashboard/documents/archive")
	@ResponseBody
	public void archiveDocuments(Model model, @RequestParam("lugar") Integer lugar, @RequestParam("tlugar") String tlugar) {
		this.archiveByOrgan(lugar, tlugar);
	}

	// GET para conseguir todos los graficos a partir de un RUCT
	@GetMapping("/dashboard/graphs/list/{ruct}")
	@ResponseBody
	public List<Grafica> getGraphDataList(@PathVariable("ruct") Integer ruct) throws ParserConfigurationException{
		Organ o = os.findByRuct(ruct);
		if(o.getId().getTlugar().equals("C")) {
			return this.gs.findLikeAmbit("%"+o.getId().getTlugar()+"%");
		}
		else {
			return this.gs.findLikeAmbitAndEstudi("%"+o.getId().getTlugar()+"%", o.getTambit());
		}	
	}
	
	//GET para conseguir todos los datos concretos de tasas para las tablas
	@GetMapping("/dashboard/graphs/rates/{lugar}")
	@ResponseBody
	public List<Indicador> getGraphDataTaxes(@PathVariable("lugar") Integer lugar) throws ParserConfigurationException{
		List<Indicador> inds = this.ids.getGraphData(lugar);
		return inds;
	}
	
	//GET para conseguir todos los datos concretos de cada gráfico
	@GetMapping({"/dashboard/graphs/inds/{ruct}", "/dashboard/graphs/inds/{ruct}/{tambit}"})
	@ResponseBody
	public List<Indicador> getGraphData(@PathVariable("ruct") Integer ruct, @PathVariable("tambit") Optional<String> tambit) throws ParserConfigurationException{
		Organ o = os.findByRuct(ruct);
		int year = Year.now().getValue();
		List<IndicadorDTO> data = new ArrayList<IndicadorDTO>();

		if(o.getTambit().equals("C")) {
			Integer centre = o.getId().getLugar();
			data = this.ies.getGraphData(centre, tambit.get(), year-10);
		}
		else {
			List<Integer> tits = this.os.getEquivalents(o.getId().getLugar(), "T");
			tits.add(o.getId().getLugar());
			Integer centre = o.getOrgan().getId().getLugar();
			data = this.ies.getGraphData(tits, centre,	o.getTambit(), year-10);
		}
		List<Indicador> inds = new ArrayList<Indicador>();
		Indicador in = new Indicador();
		String inAux = null;
		Integer count = 0;
		for(IndicadorDTO i : data) {
			count++;
			if(inAux == null) { 
				inAux = i.getDimensio()+"_"+i.getIndicador()+"_"+i.getAmbit();
				in.setIndicador(i.getIndicador());
				in.setDimension(i.getDimensio());
				in.setAmbit(i.getAmbit());
				in.getValores().add(in.new CursoValor(Integer.toString(Integer.parseInt(i.getCurs())-1)+"-"+i.getCurs(), i.getValor()));
			}
			else if(inAux.equals(i.getDimensio()+"_"+i.getIndicador()+"_"+i.getAmbit())) {
				in.getValores().add(in.new CursoValor(Integer.toString(Integer.parseInt(i.getCurs())-1)+"-"+i.getCurs(), i.getValor()));
			}
			else {
				inds.add(in);
				inAux = i.getDimensio()+"_"+i.getIndicador()+"_"+i.getAmbit();
				in = new Indicador();
				in.setIndicador(i.getIndicador());
				in.setDimension(i.getDimensio());
				in.setAmbit(i.getAmbit());
				in.getValores().add(in.new CursoValor(Integer.toString(Integer.parseInt(i.getCurs())-1)+"-"+i.getCurs(), i.getValor()));
			}
			
			if(count == data.size()) {
				inds.add(in);
			}
		}
		return inds;
	}

	@GetMapping("/dashboard/links/{ruct}")
	@ResponseBody
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public List<Link> getLinks(@PathVariable("ruct") Integer ruct) {
		List<Link> links = this.ls.findByRuct(ruct);
		for(Link l : links){
			l.setLink(publicUrl+l.getLink());
		}
		return this.ls.findByRuct(ruct);
	}

	@PostMapping("/dashboard/links/{ruct}")
	@ResponseBody
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public Link createLink(@PathVariable("ruct") Integer ruct, @RequestParam("dataExp") String dataExp) throws ParseException {
		
		String token = UUID.randomUUID().toString();
		SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
		Date date = formatter.parse(dataExp);

		Link l = new Link();
		l.setRuct(ruct);
		l.setData(new Date(System.currentTimeMillis()));
		l.setDataExp(date);
		l.setToken(token);
		l.setLink("/shared/"+token);
		l.setVisites(0);
		this.ls.save(l);

		return l;
	}

	@DeleteMapping("/dashboard/links/{id}")
	@ResponseBody
	@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
	public Integer deleteLink(@PathVariable("id") Integer id) {
		Link l = this.ls.findById(id);
		if(l != null){
			this.ls.delete(l);
			return id;
		}
		return 0;
	}
	
	// Función encargada de cargar a los managers a partir del centro y titulación
	public void loadManagers(Model model, Integer centre, Integer titulacio) {
		List<Integer> centres = Arrays.asList(centre);
		List<Integer> titulacions = Arrays.asList(titulacio);
		List<UsuarisRol> resp_centre = urs.findManagerByCentres(centres);
		Collections.sort(resp_centre, new Comparator<UsuarisRol>() {  
		    @Override  
		    public int compare(UsuarisRol a, UsuarisRol b) {  
		          
		        int OrgnomComp = a.getOrgan().getNomVal().compareTo(b.getOrgan().getNomVal()); 
		        if (OrgnomComp != 0) {  
		            return OrgnomComp;  
		        }  
		        int rolComp = a.getRol().getNomRol().compareTo(b.getRol().getNomRol());
		        if (rolComp != 0) {  
		            return rolComp;  
		        }
		        return a.getUsuari().getNom().concat(a.getUsuari().getCognoms()).compareTo(
		        		b.getUsuari().getNom().concat(b.getUsuari().getCognoms()));  
		    }  
		});
		
		List<UsuarisRol> resp_titulacio = new ArrayList<UsuarisRol>();
		if(titulacions.size() > 0) {
			resp_titulacio = urs.findManagerByTitulacions(titulacions);
			Collections.sort(resp_titulacio, new Comparator<UsuarisRol>() {  
			    @Override 
			    public int compare(UsuarisRol a, UsuarisRol b) {  
			          
			        int OrgnomComp = a.getOrgan().getNomVal().compareTo(b.getOrgan().getNomVal()); 
			        if (OrgnomComp != 0) {  
			            return OrgnomComp;  
			        }  
			        int rolComp = a.getRol().getNomRol().compareTo(b.getRol().getNomRol());
			        if (rolComp != 0) {  
			            return rolComp;  
			        }
			        return a.getUsuari().getNom().concat(a.getUsuari().getCognoms()).compareTo(
			        		b.getUsuari().getNom().concat(b.getUsuari().getCognoms()));  
			    }  
			});
		}
						
		if(resp_centre == null) {
			model.addAttribute("results", false);
		}
		else{
			model.addAttribute("results", true);
			model.addAttribute("resp_centres", resp_centre);
			model.addAttribute("resp_titulacions", resp_titulacio);
		}
	}
	
	// Función para comprobar si un usario esta permitido para hacer una acción
	public boolean isSuitable(Organ o, Usuari u) {
		if(u.isAdmin() || u.isGranted()) {
			return true;
		}
		else if(this.urs.exists(u.getUsuari(), o.getId().getTlugar(), o.getId().getLugar())) {
			return true;
		}
		
		List<Organ> titulacions = new ArrayList<Organ>();
		List<Organ> centres = new ArrayList<Organ>();
		List<UsuarisRol> rols = this.urs.findActiveRols(u);
		for(UsuarisRol rol : rols) {
			if(rol.getOrgan().getId().getTlugar().equals("C")) {
				titulacions.addAll(this.os.findTitulacionsByCentre(Arrays.asList(rol.getOrgan().getId().getLugar())));
				centres.add(rol.getOrgan());
			}
			else if(rol.getOrgan().getId().getTlugar().equals("T")) {
				titulacions.add(rol.getOrgan());
			}
		}
		
		if(centres.contains(o) || titulacions.contains(o)) {
			return true;
		}
		
		return false;
	}

	// Función para saber el tamaño de un documento concreto
	private String getSize(String path){
		FileSystemResource f = new FileSystemResource(path); 
		if(f.exists() && f.isFile()) {
			return this.bytesToHuman(f.getFile().length());
		}
		else {
			return "0 KiB";
		}
	}
	
	private String bytesToHuman(long bytes) {
	    long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
	    if (absB < 1024) {
	        return bytes + " B";
	    }
	    long value = absB;
	    CharacterIterator ci = new StringCharacterIterator("KMGTPE");
	    for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
	        value >>= 10;
	        ci.next();
	    }
	    value *= Long.signum(bytes);
	    return String.format("%.1f %ciB", value / 1024.0, ci.current());
	}
	
	private Document findByCategoriaOrgan(Integer idCategoria, Integer lugar, String tlugar) {
		CategoriaDTO categoriaDTO = new CategoriaDTO(idCategoria, lugar, tlugar);
		return dc.findByCategoriaOrgan(categoriaDTO);
	}

	private Document save(Document doc) {
		return dc.save(doc);
	}

	private Document archive(Document doc){
		return dc.archive(doc);
	}

	private void archiveByOrgan(Integer lugar, String tlugar) {
		ArchiveOrganDTO organ = new ArchiveOrganDTO(lugar, tlugar);
		dc.archiveByOrgan(organ);
	}

	private String upload(Integer idDocument, Integer idCategoria, Integer lugar, String tlugar, MultipartFile file) {
			DocumentTmpDTO doc = new DocumentTmpDTO(idDocument, idCategoria, lugar, tlugar, file);
			return dc.upload(doc);
	}
}
